diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index dd84ea78..00000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-title: ''
-labels: ''
-assignees: ''
-
----
-
-**Describe the bug**
-A clear and concise description of what the bug is.
-
-**To Reproduce**
-Steps to reproduce the behavior:
-1. Go to '...'
-2. Click on '....'
-3. Scroll down to '....'
-4. See error
-
-**Expected behavior**
-A clear and concise description of what you expected to happen.
-
-**Screenshots**
-If applicable, add screenshots to help explain your problem.
-
-**Desktop (please complete the following information):**
- - OS: [e.g. iOS]
- - Browser [e.g. chrome, safari]
- - Version [e.g. 22]
-
-**Smartphone (please complete the following information):**
- - Device: [e.g. iPhone6]
- - OS: [e.g. iOS8.1]
- - Browser [e.g. stock browser, safari]
- - Version [e.g. 22]
-
-**Additional context**
-Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index bbcbbe7d..00000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-title: ''
-labels: ''
-assignees: ''
-
----
-
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
-
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
-
-**Additional context**
-Add any other context or screenshots about the feature request here.
diff --git a/.gitignore b/.gitignore
index 9d81586a..0e66601c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,6 +32,10 @@ logs
 
 *.rdb
 
+upload
+
+
+
 
 
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 88456804..db36d11e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,119 @@
 # 更新日志 CHANGELOG
 
+## [V2.7.12.1] 2023
+> 代码生成优化
+
+###  ⭐️  New Features
+#### 代码生成可自定义包名称、文件名称
+```text
+// 自定义包名称
+config.setEntityPackage("model");
+config.setMapperPackage("dao");
+config.setDtoPackage("dto");
+config.setQueryPackage("req");
+config.setVoPackage("resp");
+config.setDtoFileName("AddDTO");
+config.setUpdateDtoFileName("UpdateDTO");
+config.setQueryFileName("Request");
+config.setVoFileName("DetailVo");
+config.setVoFileName("Response");
+```
+#### 代码生成可自定义controller中的请求路径方式
+##### 代码生成可自定义controller中的请求路径是否添加模块名称
+```text
+config.setRequestMappingModule(true);
+```
+##### 代码生成自定义请求路径名称
+> 以下展示的默认值，如果一致，可不设置，%s为实体类名称对应的值
+```text
+config.setControllerRequestMapping("%s");
+config.setAddRequestMapping("add%s");
+config.setUpdateRequestMapping("update%s");
+config.setDeleteRequestMapping("delete%s");
+config.setInfoRequestMapping("get%sInfo");
+config.setPageRequestMapping("get%sPage");
+```
+##### 请求路径方式 CAMEL：骆驼(默认)，HYPHEN：中横线，下划线：UNDERLINE，反斜杠：BACKSLASH，全小写：LOWER
+```text
+config.setRequestMappingType(RequestMappingType.CAMEL);
+```
+###### CAMEL：骆驼(默认)
+```text
+/fooBar
+/addFooBar
+/updateFooBar
+/deleteFooBar/{id}
+/getFooBarInfo/{id}
+/getFooBarPage
+```
+
+###### HYPHEN：中横线
+```text
+/foo-bar
+/add-foo-bar
+/update-foo-bar
+/delete-foo-bar/{id}
+/get-foo-bar-info/{id}
+/get-foo-bar-page
+```
+
+###### 下划线：UNDERLINE
+```text
+/foo_bar
+/add_foo_bar
+/update_foo_bar
+/delete_foo_bar/{id}
+/get_foo_bar_info/{id}
+/get_foo_bar_page
+```
+
+###### 反斜杠：BACKSLASH
+```text
+/foo/bar
+/add/foo/bar
+/update/foo/bar
+/delete/foo/bar/{id}
+/get/foo/bar/info/{id}
+/get/foo/bar/page
+```
+
+###### 全小写：LOWER
+```text
+/foobar
+/addfoobar
+/updatefoobar
+/deletefoobar/{id}
+/getfoobarinfo/{id}
+/getfoobarpage
+```
+
+## [V2.7.12] 2023.7.10  
+> 单体架构发布，更简单实用
+
+###  ⭐️  New Features
+- 多模块变更为单体架构
+- 框架代码重构，更精简，更易懂
+- springboot升级为2.7.12
+- mybatis-plus升级为3.5.3.1
+- 使用pagehelper分页，更方便
+- 去掉mapstruct，使用Spring的BeanUtils
+- 用户升级为多角色管理，更灵活
+- 使用Vue3、TS重构前端页面
+- 使用SwaggerV3
+- 去掉Shiro，使用拦截器实现登录鉴权
+- 去掉JWT，使用自定义生成token
+- 登录功能重构,登录鉴权相关放在auth
+- 登录支持用户单回话或多回话同时在线配置
+- 生成代码重构，新增addDto、updateDto、infoVo
+- 部署脚本优化
+- 新增JsonRequestBodyFilter过滤器，打印真实json请求参数
+- 新增GlobalStringTrimHandler处理器，全局字符串去除空格
+- 新增DataRangeAop数据范围切面，数据范围过滤
+- 新增TraceIdLogFilter，日志链路ID，方面定位原因
+- 新增IpRegionUtil，获取ip归属地信息
+- xss支持自定义启用禁用
+- 请求日志记录字段完善
+
 
 
 ## [V1.4.0] 2019.11.5  🚔
@@ -171,7 +285,7 @@
 - `pom.xml` 使用 `spring-boot-starter-validation` 替换 `hibernate-validator` 依赖
 
 
-## [V1.2.1-RELEASE] 2019.08.21
+## [V1.2.1-RELEASE] 2019.08.21 💯💥
 
 ###  ⭐️  New Features
 - 文件上传保存到服务器指定目录
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..5f22afbe
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,8 @@
+FROM openjdk:8
+ENV TZ=Asia/Shanghai
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+COPY target/spring-boot-plus.jar /spring-boot-plus/spring-boot-plus.jar
+WORKDIR /spring-boot-plus
+EXPOSE 8888
+ENTRYPOINT ["java","-jar","spring-boot-plus.jar"]
+
diff --git a/LICENSE b/LICENSE
index ca77898b..ab10ad73 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,201 +1,21 @@
-                                 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:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) 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
-
-      (d) 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-2029 geekidea(https://github.com/geekidea)
-
-   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.
+MIT License
+
+Copyright (c) 2023 geekidea
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/NOTICE b/NOTICE
index cf36f49b..af61a300 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,5 +1,6 @@
 GEEKIDEA Spring-Boot-Plus
-Copyright 2019-2029 The Apache Software Foundation
+Copyright 2019-2029
 
 This product includes software developed at
 The GEEKIDEA Organization (http://geekidea.io/).
+
diff --git a/README-zh.md b/README-zh.md
index 81558b8d..74bb1ea6 100644
--- a/README-zh.md
+++ b/README-zh.md
@@ -1,6 +1,6 @@
 <p align="center">
   <a href="https://github.com/geekidea/spring-boot-plus">
-   <img alt="spring-boot-plus logo" src="https://springboot.plus/img/logo.png">
+   <img alt="spring-boot-plus logo" src="https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/logo.png">
   </a>
 </p>
 <p align="center">
@@ -8,65 +8,54 @@
 </p>
 
 <p align="center">  
-  <a href="https://github.com/geekidea/spring-boot-plus/">
-    <img alt="spring-boot-plus version" src="https://img.shields.io/badge/spring--boot--plus-2.0-blue">
+  <a href="https://gitee.com/geekidea/spring-boot-plus">
+    <img alt="spring-boot-plus version" src="https://img.shields.io/badge/spring--boot--plus-2.7.18-blue">
   </a>
   <a href="https://github.com/spring-projects/spring-boot">
-    <img alt="spring boot version" src="https://img.shields.io/badge/spring%20boot-2.2.5.RELEASE-brightgreen">
+    <img alt="spring boot version" src="https://img.shields.io/badge/spring%20boot-2.7.18-brightgreen">
   </a>
-  <a href="https://www.apache.org/licenses/LICENSE-2.0">
-    <img alt="code style" src="https://img.shields.io/badge/license-Apache%202-4EB1BA.svg?style=flat-square">
+  <a href="https://cn.vuejs.org/">
+    <img alt="spring boot version" src="https://img.shields.io/badge/vue-3.2-darkgreen">
+  </a>
+  <a href="https://gitee.com/geekidea/spring-boot-plus/blob/master/LICENSE">
+    <img alt="code style" src="https://img.shields.io/badge/license-MIT-green">
   </a>
 </p>
 
-### spring-boot-plus是一套集成spring boot常用开发组件的后台快速开发框架
-> Spring-Boot-Plus是易于使用，快速，高效，功能丰富，开源的spring boot 脚手架.
+### spring-boot-plus是一套前后端分离单体结构的快速开发框架
 
-> 前后端分离,专注于后端服务
+## 开源协议 MIT-License
+### 任何公司或个人，均可进行商业使用，无需授权！
+### 任何公司或个人，均可进行商业使用，无需授权！
+### 任何公司或个人，均可进行商业使用，无需授权！
 
-## 目标
-> 每个人都可以独立、快速、高效地开发项目！
+## 前端项目地址  [VUE3+TS](https://gitee.com/geekidea/spring-boot-plus-vue3)
 
 #### [GITHUB](https://github.com/geekidea/spring-boot-plus) | [GITEE](https://gitee.com/geekidea/spring-boot-plus)
 
 #### [springboot.plus](http://springboot.plus)
 
+
+## 微信交流群
+![微信群](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/wechat-group.png)
+
+## 业务咨询
+![业务咨询](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/service.png)
+
+
 ### 主要特性
 - 集成spring boot 常用开发组件集、公共配置、AOP日志等
-- Maven多模块架构
+- 单体架构，更快更简单
+- 提供PC管理端与APP端代码接口
 - 集成mybatis plus快速dao操作
-- 快速生成后台代码: entity/param/vo/controller/service/mapper/xml
+- 快速生成后台代码: entity/dto/query/vo/controller/service/mapper/xml
 - 集成Swagger/Knife4j，可自动生成api文档
-- 集成jwt、shiro权限控制
 - 集成Redis缓存
 - 集成HikariCP连接池，JDBC性能和慢查询检测
-- 集成spring boot admin，实时检测项目运行情况
-- 使用assembly maven插件进行不同环境打包部署,包含启动、重启命令，配置文件提取到外部config目录
-
-## [V2.0视频介绍](https://www.bilibili.com/video/BV16A41187XE/)
-<p align="center">
-  <a href="https://www.bilibili.com/video/BV16A41187XE/">
-   <img alt="spring-boot-plus videos" src="https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/videos-acfun-ui.png">
-  </a>
-</p>
 
-## 项目结构
-```text
-    └── spring-boot-plus
-    ├── admin               SpringBootAdmin Server模块
-    ├── bootstrap           spring-boot-plus 启动模块
-    ├── config              配置模块
-    ├── distribution        打包模块
-    ├── docs                文档目录
-    ├── example             示例模块，自己的业务可新建多个模块处理
-    ├── framework           框架核心模块
-    ├── generator           代码生成模块
-    ├── scheduled           任务调度模块
-    └── system              系统模块
-```
 
 ### 项目环境 
-中间件 | 版本 |  备注
+名称 | 版本 |  备注
 -|-|-
 JDK | 1.8+ | JDK1.8及以上 |
 MySQL | 5.7+ | 5.7及以上 |
@@ -75,391 +64,54 @@ Redis | 3.2+ |  |
 ### 技术选型 
 技术 | 版本 |  备注
 -|-|-
-Spring Boot | 2.2.0.RELEASE | 最新发布稳定版 |
-Spring Framework | 5.2.0.RELEASE | 最新发布稳定版 |
-Spring Boot Admin| 2.2.2 | 管理和监控SpringBoot应用程序 |
-Mybatis | 3.5.3 | 持久层框架 |
-Mybatis Plus | 3.3.1 | mybatis增强框架 |
-HikariCP | 3.4.2 | 数据源 |
-Fastjson | 1.2.67 | JSON处理工具集 |
-Swagger2 | 2.9.2 | api文档生成工具 |
-Knife4j | 2.0.2 | api文档生成工具 |
-commons-lang3 | 3.9 | 常用工具包 |
-commons-io | 2.6 | IO工具包 |
-commons-codec | 1.14 | 加密解密等工具包 |
-commons-collections4 | 4.4 | 集合工具包 |
-reflections | 0.9.9 | 反射工具包 |
-hibernate-validator | 6.0.18.Final | 后台参数校验注解 |
-Shiro | 1.5.1 | 权限控制 |
-JWT | 3.10.1 | JSON WEB TOKEN |
-hutool-all | 5.2.4 | 常用工具集 |
-lombok | 1.18.12 | 注解生成Java Bean等工具 |
-mapstruct | 1.3.1.Final | 对象属性复制工具 |
-
-### 项目链路图
-![项目调用链路图](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/spring-boot-plus-link-diagram.jpg)
-
-### [CHANGELOG](https://github.com/geekidea/spring-boot-plus/blob/master/CHANGELOG.md)
-
-## 快速开始
-### 克隆 spring-boot-plus
-```bash
-git clone https://gitee.com/geekidea/spring-boot-plus.git
-cd spring-boot-plus
-```
-
-### Maven 构建
-> 默认使用dev环境,对应配置文件：application-dev.yml
-
-```bash
-mvn clean package -Pdev
-```
-
-
-## 5分钟完成增删改查
-
-### 1. 创建数据库表
-```sql
--- ----------------------------
--- Table structure for foo_bar
--- ----------------------------
-DROP TABLE IF EXISTS `foo_bar`;
-CREATE TABLE `foo_bar`
-(
-    `id`            bigint(20)  NOT NULL COMMENT '主键',
-    `name`          varchar(20) NOT NULL COMMENT '名称',
-    `foo`           varchar(20)          DEFAULT NULL COMMENT 'Foo',
-    `bar`           varchar(20) NOT NULL COMMENT 'Bar',
-    `remark`        varchar(200)         DEFAULT NULL COMMENT '备注',
-    `state`         int(11)     NOT NULL DEFAULT '1' COMMENT '状态，0：禁用，1：启用',
-    `version`       int(11)     NOT NULL DEFAULT '0' COMMENT '版本',
-    `create_time`   timestamp   NULL     DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-    `update_time`   timestamp   NULL     DEFAULT NULL COMMENT '修改时间',
-    PRIMARY KEY (`id`)
-) ENGINE = InnoDB
-  DEFAULT CHARSET = utf8mb4
-  COLLATE = utf8mb4_general_ci COMMENT ='FooBar';
-
--- ----------------------------
--- Records of foo_bar
--- ----------------------------
-INSERT INTO foo_bar (id, name, foo, bar, remark, state, version, create_time, update_time) 
-    VALUES (1, 'FooBar', 'foo', 'bar', 'remark...', 1, 0, '2019-11-01 14:05:14', null);
-INSERT INTO foo_bar (id, name, foo, bar, remark, state, version, create_time, update_time) 
-    VALUES (2, 'HelloWorld', 'hello', 'world', null, 1, 0, '2019-11-01 14:05:14', null);
-
-```
-
-
-### 2.使用代码生成器生成增删改查代码
-> 代码生成入口类，在generator模块中
-
-```text
-spring-boot-plus/generator/src/main/java/io/geekidea/springbootplus/generator/SpringBootPlusGenerator.java
-```
-
-```java
-/**
- * spring-boot-plus代码生成器入口类
- *
- * @author geekidea
- * @date 2019-10-22
- **/
-@Component
-public class SpringBootPlusGenerator {
-
-    /**
-     * 生成代码
-     * @param args
-     */
-    public static void main(String[] args) {
-        GeneratorProperties generatorProperties = new GeneratorProperties();
-
-        // 设置基本信息
-        generatorProperties
-                .setMavenModuleName("example")
-                .setParentPackage("com.example")
-                .setModuleName("foobar")
-                .setAuthor("geekidea")
-                .setFileOverride(true);
-
-        // 设置表信息
-        generatorProperties.addTable("foo_bar","id");
-
-        // 数据源配置
-        generatorProperties.getDataSourceConfig()
-                .setDbType(DbType.MYSQL)
-                .setUsername("root")
-                .setPassword("root")
-                .setDriverName("com.mysql.jdbc.Driver")
-                .setUrl("jdbc:mysql://localhost:3306/spring_boot_plus?useUnicode=true&characterEncoding=UTF-8&useSSL=false");
-
-        // 生成配置
-        generatorProperties.getGeneratorConfig()
-                .setGeneratorStrategy(GeneratorStrategy.SINGLE)
-                .setGeneratorEntity(true)
-                .setGeneratorController(true)
-                .setGeneratorService(true)
-                .setGeneratorServiceImpl(true)
-                .setGeneratorMapper(true)
-                .setGeneratorMapperXml(true)
-                .setGeneratorPageParam(true)
-                .setGeneratorQueryVo(true)
-                .setRequiresPermissions(true)
-                .setPageListOrder(true)
-                .setParamValidation(true)
-                .setSwaggerTags(true)
-                .setOperationLog(true);
-
-        // 全局配置
-        generatorProperties.getMybatisPlusGeneratorConfig().getGlobalConfig()
-                .setOpen(true)
-                .setSwagger2(true)
-                .setIdType(IdType.AUTO)
-                .setDateType(DateType.ONLY_DATE);
-
-        // 策略配置
-        generatorProperties.getMybatisPlusGeneratorConfig().getStrategyConfig()
-                .setNaming(NamingStrategy.underline_to_camel)
-                .setColumnNaming(NamingStrategy.underline_to_camel)
-                .setEntityLombokModel(true)
-                .setRestControllerStyle(true)
-                .setControllerMappingHyphenStyle(true)
-                .setVersionFieldName(GeneratorConstant.VERSION)
-                .setLogicDeleteFieldName(GeneratorConstant.DELETED);
-
-        // 生成代码
-        CodeGenerator codeGenerator = new CodeGenerator();
-        codeGenerator.generator(generatorProperties);
-    }
-}
-```
-
-#### Code Generator Templates
-> 使用Velocity模版生成代码，可自定义修改代码生成模版
-
-```text
-spring-boot-plus/generator/src/main/resources
-```
-
-```text
-└── templates
-    ├── controller.java.vm      控制器代码生成模版
-    ├── entity.java.vm          实体类代码生成模版
-    ├── mapper.java.vm          mapper代码生成模版
-    ├── mapper.xml.vm           mapper xml 代码生成模版
-    ├── pageParam.java.vm       分页参数代码生成模版
-    ├── queryVo.java.vm         查询结果代码生成模版
-    ├── service.java.vm         服务接口代码生成模版
-    └── serviceImpl.java.vm     服务实现代码生成模版
-```
-
-#### 生成的代码结构
-
-```text
-└── src
-    └── main
-        ├── java
-        │   └── com
-        │       └── example
-        │           └── foobar
-        │               ├── controller
-        │               │   └── FooBarController.java
-        │               ├── entity
-        │               │   └── FooBar.java
-        │               ├── mapper
-        │               │   └── FooBarMapper.java
-        │               ├── param
-        │               │   └── FooBarPageParam.java
-        │               ├── service
-        │               │   ├── FooBarService.java
-        │               │   └── impl
-        │               │       └── FooBarServiceImpl.java
-        │               └── vo
-        │                   └── FooBarQueryVo.java
-        └── resources
-            └── mapper
-                └── foobar
-                    └── FooBarMapper.xml
-```
-
-### 3. 启动SpringBootAdmin
-> SpringBootAdmin Server启动类，在admin模块中  [http://localhost:8000](http://localhost:8000)
-
-```text
-spring-boot-plus/admin/src/main/java/io/geekidea/springbootplus/admin/SpringBootPlusAdminApplication
-```
-
-```java
-/**
- * Spring Boot Admin Bootstrap Main Class
- *
- * @author geekidea
- * @date 2020/3/20
- **/
-@Slf4j
-@Configuration
-@EnableAutoConfiguration
-@EnableAdminServer
-@SpringBootApplication
-public class SpringBootPlusAdminApplication {
-
-    public static void main(String[] args) {
-        ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusAdminApplication.class, args);
-        ConfigurableEnvironment environment = context.getEnvironment();
-        String serverPort = environment.getProperty("server.port");
-        log.info("SpringBootAdmin: http://localhost:" + serverPort);
-    }
-
-}
-```
-
-### 4. 启动项目
-> 项目入口类，在bootstrap模块中  [http://localhost:8888](http://localhost:8888)
-
-```text
-spring-boot-plus/bootstrap/src/main/java/io/geekidea/springbootplus/SpringBootPlusApplication.java
-```
-
-```java
-/**
- * spring-boot-plus 项目启动入口
- *
- * @author geekidea
- * @since 2018-11-08
- */
-@EnableAsync
-@EnableScheduling
-@EnableTransactionManagement
-@EnableConfigurationProperties
-@ServletComponentScan
-@MapperScan({"io.geekidea.springbootplus.**.mapper", "com.example.**.mapper"})
-@SpringBootApplication(scanBasePackages = {"io.geekidea.springbootplus", "com.example"})
-public class SpringBootPlusApplication {
-
-    public static void main(String[] args) {
-        // 启动spring-boot-plus
-        ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusApplication.class, args);
-        // 打印项目信息
-        PrintApplicationInfo.print(context);
-        // 打印项目提示
-        PrintApplicationInfo.printTip(context);
-    }
-
-}
-```
-
-### 5. 访问项目Swagger文档
-[http://47.105.159.10:8888/api/swagger-ui.html](http://47.105.159.10:8888/api/swagger-ui.html)
-![swagger-ui.png](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/swagger-ui.png)
-![swagger-ui-1.png](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/swagger-ui-1.png)
-
-### 6. 访问Knife4j文档
-[http://47.105.159.10:8888/api/doc.html](http://47.105.159.10:8888/api/doc.html)
-![knife4j.png](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/knife4j.png)
-![knife4j-1.png](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/knife4j-1.png)
-
-## CentOS快速安装环境/构建/部署/启动spring-boot-plus项目
-### 1. 下载安装脚本
-> 安装 `jdk`, `git`, `maven`, `redis`, `mysql`
-
-```bash
-wget -O download-install-all.sh https://springboot.plus/bin/download-install-all.sh
-```
-
-### 2. 运行安装脚本
-```bash
-sh download-install-all.sh
-```
-
-### 3. 修改MySQL密码
-```bash
-ALTER USER 'root'@'localhost' IDENTIFIED BY 'Springbootplus666!';
-exit
-mysql -uroot -pSpringbootplus666!
-```
-
-### 4. 导入MySQL脚本
-```bash
-create database if not exists spring_boot_plus character set utf8mb4;
-use spring_boot_plus;
-source /root/mysql_spring_boot_plus.sql;
-show tables;
-exit
-```
-
-### 5. 下载部署脚本 `deploy.sh`
-```bash
-wget -O deploy.sh https://springboot.plus/bin/deploy.sh
-```
-
-### 6. 执行脚本
-```bash
-sh deploy.sh
-```
-
-### 7. 查看项目运行日志
-```bash
-tail -f -n 1000 /spring-boot-plus-server-2.0/logs/spring-boot-plus.log
-```
-
-
-## spring-boot-plus Views
-
-### spring-boot-plus IDEA Sources Views
-
-![spring-boot-plus-idea](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/idea.png)
-
-### [Spring Boot Admin Instances](http://47.105.159.10:8000/instances/11090f218c47/details)
-<p>
-    <a href="http://47.105.159.10:8000/instances/11090f218c47/details">
-        <img src="https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/springbootadmin.png" alt="spring-boot-admin instances">
-    </a>
-</p>
-
-### [Spring Boot Admin Statistics](http://47.105.159.10:8000/instances/11090f218c47/details)
-<p>
-    <a href="http://47.105.159.10:8000/instances/11090f218c47/details">
-        <img src="https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/springbootadmin1.png" alt="spring-boot-admin statistics">
-    </a>
-</p>
-
-### [Spring Boot Admin Log](http://47.105.159.10:8000/instances/11090f218c47/logfile)
-<p>
-    <a href="http://47.105.159.10:8000/instances/11090f218c47/logfile">
-        <img src="https://spring-boot-plus.gitee.io/img/home/spring-boot-admin-log.png" alt="spring-boot-admin log">
-    </a>
-</p>
-
-## spring-boot-plus-vue 前端项目
-### [GITHUB-REPO](https://github.com/geekidea/spring-boot-plus-vue)
-### [VUE演示地址](http://47.105.159.10/)
-#### VUE主页
-![VUE主页](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/springbootplusvue.png)
+spring boot | 2.7.18 |
+Mybatis | 3.5.13 | DAO Framework |
+mybatis-plus | 3.5.4.1 | mybatis增强框架 |
+fastjson2 | 2.0.42 | JSON处理工具集 |
+Swagger | V3 | Swagger文档 |
+knife4j | 4.3.0 | api文档生成工具 |
+commons-lang3 | 3.14.0 | 常用工具包 |
+commons-io | 2.15.0 | IO工具包 |
+commons-codec | 1.16.0 | 加密解密等工具包 |
+commons-collections4 | 4.4.4 | 集合工具包 |
+hibernate-validator | 6.2.5.Final | 后台参数校验注解 |
+hutool-all | 5.8.23 | 常用工具集 |
+lombok | 1.18.30 | 注解生成Java Bean等工具 |
+
+### [CHANGELOG](https://gitee.com/geekidea/spring-boot-plus/blob/master/CHANGELOG.md)
+
+
+### 4. 访问项目Swagger文档
+[http://localhost:8888/swagger-ui/index.html](http://localhost:8888/swagger-ui/index.html)
+![swagger-ui.png](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/swagger-v3.png)
+
+### 5. 访问Knife4j文档
+[http://localhost:8888/doc.html](http://localhost:8888/doc.html)
+![knife4j.png](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/knife4j-doc.png)
+
+
+## spring-boot-plus-vue3 前端项目
+### [https://gitee.com/geekidea/spring-boot-plus-vue3](https://gitee.com/geekidea/spring-boot-plus-vue3)
 #### 系统用户列表
-![系统用户列表](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/springbootplusvue-1.png)
+![系统用户列表](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/user-manager.png)
 #### 系统角色列表
-![系统角色模块](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/springbootplusvue-2.png)
+![系统角色列表](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/role-manager.png)
 #### 系统菜单列表
-![系统菜单列表](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/springbootplusvue-3.png)
-
-## spring-boot-plus 视频  :movie_camera:
-- [V2.x 视频介绍慢放版](https://www.bilibili.com/video/BV1HQ4y1M73i/)
-- [V1.x 5分钟完成增删改查](https://www.bilibili.com/video/av67401204)
-- [CentOS 快速安装 JDK/Git/Maven/Redis/MySQL](https://www.bilibili.com/video/av67218836/)
-- [CentOS 快速部署/构建/打包/运行项目](https://www.bilibili.com/video/av67218970/)
-
+![系统菜单列表](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/menu-manager.png)
+#### 系统部门列表
+![系统部门列表](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/dept-manager-dark.png)
+#### 系统日志
+![系统日志列表](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/log-manager.png)
+![系统日志详情](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/log-detail.png)
+#### 个人中心
+![个人中心](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/profile.png)
 
 ## 联系
-QQ 625301326| 微信公众号 geekideaio|  今日头条 GeekIdea
--|-|-
-![spring-boot-plus QQ Group](https://spring-boot-plus.gitee.io/img/spring-boot-plus-qq-group.png) | ![Wechat Official Account](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/geekidea-wechat-official.jpg) | ![toutiao](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/geekidea-toutiao.jpeg) |
+QQ群 625301326| 微信公众号 geekideaio|
+-|-|
+![spring-boot-plus QQ Group](https://spring-boot-plus.gitee.io/img/spring-boot-plus-qq-group.png) | ![Wechat Official Account](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/geekidea-wechat-official.jpg) |
 
-## 赞赏
-请作者喝咖啡，让代码飞一会儿！
-                          
-![geekidea-wechat-donate](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/geekidea-wechat-donate.jpeg)
 
 ## License
-spring-boot-plus is under the Apache 2.0 license. See the [LICENSE](https://github.com/geekidea/spring-boot-plus/blob/master/LICENSE) file for details.
+spring-boot-plus is under the MIT-License. See the [LICENSE](https://gitee.com/geekidea/spring-boot-plus/blob/master/LICENSE) file for details.
 
diff --git a/README.md b/README.md
index 92ef1fbf..af50bb19 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 <p align="center">
   <a href="https://github.com/geekidea/spring-boot-plus">
-   <img alt="spring-boot-plus logo" src="https://springboot.plus/img/logo.png">
+   <img alt="spring-boot-plus logo" src="https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/logo.png">
   </a>
 </p>
 <p align="center">
@@ -9,13 +9,16 @@
 
 <p align="center">  
   <a href="https://github.com/geekidea/spring-boot-plus/">
-    <img alt="spring-boot-plus version" src="https://img.shields.io/badge/spring--boot--plus-2.0-blue">
+    <img alt="spring-boot-plus version" src="https://img.shields.io/badge/spring--boot--plus-2.7.18-blue">
   </a>
   <a href="https://github.com/spring-projects/spring-boot">
-    <img alt="spring boot version" src="https://img.shields.io/badge/spring%20boot-2.2.5.RELEASE-brightgreen">
+    <img alt="spring boot version" src="https://img.shields.io/badge/spring%20boot-2.7.18-brightgreen">
   </a>
-  <a href="https://www.apache.org/licenses/LICENSE-2.0">
-    <img alt="code style" src="https://img.shields.io/badge/license-Apache%202-4EB1BA.svg?style=flat-square">
+  <a href="https://cn.vuejs.org/">
+    <img alt="spring boot version" src="https://img.shields.io/badge/vue-3.2-darkgreen">
+  </a>
+  <a href="https://github.com/geekidea/spring-boot-plus/blob/master/LICENSE">
+    <img alt="code style" src="https://img.shields.io/badge/license-MIT-green">
   </a>
 </p>
 
@@ -24,11 +27,14 @@
 ### A **easy-to-use**, **high-speed**, **high-efficient**, **feature-rich**, **open source** spring boot scaffolding.
 > spring-boot-plus is a background rapid development framework that integrates spring boot common development components.
 
-> Front-end and back-end separation, focusing on back-end services
-
 ## Purpose
 > Everyone can develop projects independently, quickly and efficiently！
 
+## Open source License MIT-License
+> Any individual or company can conduct secondary development based on this framework for commercial use without authorization!
+
+## Vue Project  [VUE3+TS](https://gitee.com/geekidea/spring-boot-plus-vue3)
+
 #### [GITHUB](https://github.com/geekidea/spring-boot-plus) | [GITEE](https://gitee.com/geekidea/spring-boot-plus)
 
 #### [中文文档](https://github.com/geekidea/spring-boot-plus/blob/master/README-zh.md)
@@ -37,40 +43,61 @@
 
 ## Features
 - Integrated spring boot common development component set, common configuration, AOP log, etc
-- Maven Module Project
+- Maven Project
 - Integrated mybatis-plus fast dao operation
 - Quickly generate background code:entity/param/vo/controller/service/mapper/xml
 - Integrated Swagger/Knife4j, automatic generation of api documents
-- Integrated JWT,Shiro permission control
 - Integrated Redis Cache
 - Integration HikariCP connection pool, A solid, high-performance, JDBC connection pool at last.
-- Integrated Spring Boot Admin, real-time detection of project operation
-- Integrate maven-assembly-plugin for different environment package deployment, including startup and restart commands, and extract configuration files to external config directory
-
-## [V2.0 Video Introduction](https://www.bilibili.com/video/BV16A41187XE/)
-<p align="center">
-  <a href="https://www.bilibili.com/video/BV16A41187XE/">
-   <img alt="spring-boot-plus videos" src="https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/videos-acfun-ui.png">
-  </a>
-</p>
 
-## Project structure
+## Source code directory structure
 ```text
-  spring-boot-plus
-    ├── admin           SpringBootAdmin Server Module          
-    ├── bootstrap       spring-boot-plus Bootstrap Module    
-    ├── config          Config Module
-    ├── distribution    Maven assembly Module        
-    ├── docs            Document
-    ├── example         Example Module
-    ├── framework       Framework Core Module    
-    ├── generator       Code Generator Module    
-    ├── scheduled       Scheduled Module
-    └── system          System Manager Module
+spring-boot-plus
+├── docs
+│ ├── bin
+│ │ └── install
+│ ├── config
+│ ├── db
+│ └── img
+├── logs
+└── src
+    ├── main
+    │ ├── java
+    │ │ └── io
+    │ │     └── geekidea
+    │ │         └── boot
+    │ │             ├── auth
+    │ │             ├── common
+    │ │             ├── config
+    │ │             ├── demo
+    │ │             ├── framework
+    │ │             ├── system
+    │ │             ├── user
+    │ │             └── util
+    │ │             └── SpringBootPlusApplication.java
+    │ └── resources
+    │     ├── mapper
+    │     └── static
+    │     ├── application-dev.yml
+    │     ├── application-prod.yml
+    │     ├── application-test.yml
+    │     ├── application.yml
+    │     ├── banner.txt
+    │     ├── ip2region.xdb
+    │     ├── logback-spring.xml
+    └── test
+        ├── java
+        │ └── io
+        │     └── geekidea
+        │         └── boot
+        │             ├── generator
+        │             └── system
+        └── resources
+            └── templates
 ```
 
 ### Project Environment 
-Middleware | Version |  Remark
+Name | Version |  Remark
 -|-|-
 JDK | 1.8+ | JDK1.8 and above |
 MySQL | 5.7+ | 5.7 and above |
@@ -79,29 +106,22 @@ Redis | 3.2+ |  |
 ### Technology stack 
 Component| Version |  Remark
 -|-|-
-Spring Boot | 2.2.5.RELEASE | Latest release stable version |
-Spring Framework | 5.2.4.RELEASE | Latest release stable version |
-Spring Boot Admin| 2.2.2 | Manage and monitor spring boot applications |
-Mybatis | 3.5.3 | DAO Framework |
-Mybatis Plus | 3.3.1 | mybatis Enhanced framework |
-HikariCP | 3.4.2 | DataSource |
-Fastjson | 1.2.67 | JSON processing toolset |
-Swagger2 | 2.9.2 | Api document generation tool |
-Knife4j | 2.0.2 | Api document generation tool |
-commons-lang3 | 3.9 | Apache language toolkit |
-commons-io | 2.6 | Apache IO Toolkit |
-commons-codec | 1.14 | Apache Toolkit such as encryption and decryption |
-commons-collections4 | 4.4 | Apache collections toolkit |
-reflections | 0.9.9 | Reflection Toolkit  |
-hibernate-validator | 6.0.18.Final | Validator toolkit |
-Shiro | 1.5.1 | Permission control |
-JWT | 3.10.1 | JSON WEB TOKEN |
-hutool-all | 5.2.4 | Common toolset |
-lombok | 1.18.12 | Automatically plugs |
-mapstruct | 1.3.1.Final | Object property replication tool |
+Spring Boot | 2.7.18 |
+Mybatis | 3.5.13 | DAO Framework |
+Mybatis Plus | 3.5.4.1 | mybatis Enhanced framework |
+Fastjson | 2.0.42 | JSON processing toolset |
+Swagger | V3 | Api document generation tool |
+Knife4j | 4.3.0 | Api document generation tool |
+commons-lang3 | 3.14.0 | Apache language toolkit |
+commons-io | 2.15.0 | Apache IO Toolkit |
+commons-codec | 1.16.0 | Apache Toolkit such as encryption and decryption |
+commons-collections4 | 4.4.4 | Apache collections toolkit |
+hibernate-validator | 6.2.5.Final | Validator toolkit |
+hutool-all | 5.8.23 | Common toolset |
+lombok | 1.18.30 | Automatically plugs |
 
 ### Project Link Diagram
-![项目调用链路图](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/spring-boot-plus-link-diagram.jpg)
+![项目调用链路图](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/system-link.jpg)
 
 ### [CHANGELOG](https://github.com/geekidea/spring-boot-plus/blob/master/CHANGELOG.md)
 
@@ -144,13 +164,24 @@ CREATE TABLE `foo_bar`
   DEFAULT CHARSET = utf8mb4
   COLLATE = utf8mb4_general_ci COMMENT ='FooBar';
 
+create table foo_bar
+(
+    id          bigint                               not null comment 'ID'
+        primary key,
+    name        varchar(20)                          not null comment 'Name',
+    foo         varchar(100)                         null comment 'Foo',
+    bar         varchar(100)                         null comment 'Bar',
+    remark      varchar(200)                         null comment 'Remark',
+    status      tinyint(1) default 1                 not null comment 'Status，0：Disable，1：Enable',
+    create_time timestamp  default CURRENT_TIMESTAMP null comment 'Create Time',
+    update_time timestamp                            null comment 'Update Time'
+)
+    comment 'FooBar';
+
 -- ----------------------------
 -- Records of foo_bar
 -- ----------------------------
-INSERT INTO foo_bar (id, name, foo, bar, remark, state, version, create_time, update_time) 
-    VALUES (1, 'FooBar', 'foo', 'bar', 'remark...', 1, 0, '2019-11-01 14:05:14', null);
-INSERT INTO foo_bar (id, name, foo, bar, remark, state, version, create_time, update_time) 
-    VALUES (2, 'HelloWorld', 'hello', 'world', null, 1, 0, '2019-11-01 14:05:14', null);
+INSERT INTO foo_bar (id, name, foo, bar, remark, status, create_time, update_time) VALUES (1, 'FooBar', 'Foo', 'Bar', null, 1, '2023-07-01 21:01:10', null);
 
 ```
 
@@ -158,7 +189,7 @@ INSERT INTO foo_bar (id, name, foo, bar, remark, state, version, create_time, up
 > Code generation entry class, in the generator module
 
 ```text
-spring-boot-plus/generator/src/main/java/io/geekidea/springbootplus/generator/SpringBootPlusGenerator.java
+src/test/java/io/geekidea/boot/generator/Generator.java
 ```
 
 ```java
@@ -166,161 +197,82 @@ spring-boot-plus/generator/src/main/java/io/geekidea/springbootplus/generator/Sp
  * spring-boot-plus Code Generator Main Class
  *
  * @author geekidea
- * @date 2019-10-22
+ * @date 2022/3/16
  **/
-@Component
-public class SpringBootPlusGenerator {
-
-    /**
-     * Generator Code
-     * @param args
-     */
-    public static void main(String[] args) {
-        GeneratorProperties generatorProperties = new GeneratorProperties();
-
-        // Common configuration
-        generatorProperties
-                .setMavenModuleName("example")
-                .setParentPackage("com.example")
-                .setModuleName("foobar")
-                .setAuthor("geekidea")
-                .setFileOverride(true);
-
-        // generator Table
-        generatorProperties.addTable("foo_bar","id");
-
-        // DataSourceConfig
-        generatorProperties.getDataSourceConfig()
-                .setDbType(DbType.MYSQL)
-                .setUsername("root")
-                .setPassword("root")
-                .setDriverName("com.mysql.jdbc.Driver")
-                .setUrl("jdbc:mysql://localhost:3306/spring_boot_plus?useUnicode=true&characterEncoding=UTF-8&useSSL=false");
-
-        // GeneratorConfig
-        generatorProperties.getGeneratorConfig()
-                .setGeneratorStrategy(GeneratorStrategy.SINGLE)
-                .setGeneratorEntity(true)
-                .setGeneratorController(true)
-                .setGeneratorService(true)
-                .setGeneratorServiceImpl(true)
-                .setGeneratorMapper(true)
-                .setGeneratorMapperXml(true)
-                .setGeneratorPageParam(true)
-                .setGeneratorQueryVo(true)
-                .setRequiresPermissions(true)
-                .setPageListOrder(true)
-                .setParamValidation(true)
-                .setSwaggerTags(true)
-                .setOperationLog(true);
-
-        // GlobalConfig
-        generatorProperties.getMybatisPlusGeneratorConfig().getGlobalConfig()
-                .setOpen(true)
-                .setSwagger2(true)
-                .setIdType(IdType.AUTO)
-                .setDateType(DateType.ONLY_DATE);
-
-        // StrategyConfig
-        generatorProperties.getMybatisPlusGeneratorConfig().getStrategyConfig()
-                .setNaming(NamingStrategy.underline_to_camel)
-                .setColumnNaming(NamingStrategy.underline_to_camel)
-                .setEntityLombokModel(true)
-                .setRestControllerStyle(true)
-                .setControllerMappingHyphenStyle(true)
-                .setVersionFieldName(GeneratorConstant.VERSION)
-                .setLogicDeleteFieldName(GeneratorConstant.DELETED);
-
-        // Code Generator 
-        CodeGenerator codeGenerator = new CodeGenerator();
-        codeGenerator.generator(generatorProperties);
+public class Generator {
+
+    public static void main(String[] args) throws Exception {
+        GeneratorConfig config = new GeneratorConfig();
+        // 项目信息配置
+        config.setParentPackage("io.geekidea.boot" )
+                .setModuleName("foobar" )
+                .setAuthor("geekidea" );
+        // 表名称和需要去掉的表前缀
+        config.setTableNames("foo_bar" )
+                .setTablePrefix("");
+        // 是否覆盖已有文件
+        config.setFileOverride(true);
+        // 是否只更新实体类
+        config.setOnlyOverrideEntity(false);
+        GenerateHandler handler = new GenerateHandler();
+        handler.generator(config);
     }
 }
 ```
 
-#### Code Generator Templates
-> Use Velocity template to generate code, you can customize and modify the code to generate template
-
-```text
-spring-boot-plus/generator/src/main/resources
-```
-```text
-└── templates
-    ├── controller.java.vm      controller generator template
-    ├── entity.java.vm          entity generator template
-    ├── mapper.java.vm          mapper  generator template
-    ├── mapper.xml.vm           mapper xml  generator template
-    ├── pageParam.java.vm       page param  generator template
-    ├── queryVo.java.vm         query vo  generator template
-    ├── service.java.vm         service  generator template
-    └── serviceImpl.java.vm     service implement  generator template
-```
-
 #### Generated code structure
 
 ```text
-└── src
-    └── main
-        ├── java
-        │   └── com
-        │       └── example
-        │           └── foobar
-        │               ├── controller
-        │               │   └── FooBarController.java
-        │               ├── entity
-        │               │   └── FooBar.java
-        │               ├── mapper
-        │               │   └── FooBarMapper.java
-        │               ├── param
-        │               │   └── FooBarPageParam.java
-        │               ├── service
-        │               │   ├── FooBarService.java
-        │               │   └── impl
-        │               │       └── FooBarServiceImpl.java
-        │               └── vo
-        │                   └── FooBarQueryVo.java
-        └── resources
-            └── mapper
-                └── foobar
-                    └── FooBarMapper.xml
+├── controller
+│ └── FooBarController.java
+├── dto
+│ ├── FooBarDto.java
+│ └── FooBarUpdateDto.java
+├── entity
+│ └── FooBar.java
+├── mapper
+│ └── FooBarMapper.java
+├── query
+│ └── FooBarQuery.java
+├── service
+│ ├── FooBarService.java
+│ └── impl
+│     └── FooBarServiceImp.java
+└── vo
+    ├── FooBarVo.java
+    └── FooBarVo.java
+
+resources
+└── mapper
+    └── foobar
+        └── FooBarMapper.xml    
 ```
 
-### 3. Bootstrap SpringBootAdmin Server
-> SpringBootAdmin Server Main Class，admin module  [http://localhost:8000](http://localhost:8000)
+#### Code Generator Templates
+> Use Velocity template to generate code, you can customize and modify the code to generate template
 
 ```text
-spring-boot-plus/admin/src/main/java/io/geekidea/springbootplus/admin/SpringBootPlusAdminApplication
+src/test/resources
+└── templates
+    ├── addDto.java.vm          Add DTO generator template
+    ├── controller.java.vm      Controller generator template
+    ├── entity.java.vm          Entity generator template
+    ├── infoVo.java.vm          Detail VO generator template
+    ├── mapper.java.vm          Mapper  generator template
+    ├── mapper.xml.vm           Mapper xml  generator template
+    ├── query.java.vm           Page Query  generator template
+    ├── service.java.vm         Service  generator template
+    ├── serviceImpl.java.vm     Service implement  generator template
+    ├── updateDto.java.vm       Update DTO generator template
+    └── vo.java.vm              List VO generator template
 ```
 
-```java
-/**
- * Spring Boot Admin Bootstrap Main Class
- *
- * @author geekidea
- * @date 2020/3/20
- **/
-@Slf4j
-@Configuration
-@EnableAutoConfiguration
-@EnableAdminServer
-@SpringBootApplication
-public class SpringBootPlusAdminApplication {
-
-    public static void main(String[] args) {
-        ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusAdminApplication.class, args);
-        ConfigurableEnvironment environment = context.getEnvironment();
-        String serverPort = environment.getProperty("server.port");
-        log.info("SpringBootAdmin: http://localhost:" + serverPort);
-    }
-
-}
-```
 
-### 4. Startup Project
-> Project Main Class，bootstrap module  [http://localhost:8888](http://localhost:8888)
+### 3. Startup Project
+> Project Main Class: SpringBootPlusApplication  [http://localhost:8888](http://localhost:8888)
 
 ```text
-spring-boot-plus/bootstrap/src/main/java/io/geekidea/springbootplus/SpringBootPlusApplication.java
+src/main/java/io/geekidea/boot/SpringBootPlusApplication.java
 ```
 
 ```java
@@ -328,141 +280,66 @@ spring-boot-plus/bootstrap/src/main/java/io/geekidea/springbootplus/SpringBootPl
  * spring-boot-plus Project Main Class
  *
  * @author geekidea
- * @since 2018-11-08
+ * @date 2022-3-16
  */
 @EnableAsync
-@EnableScheduling
-@EnableTransactionManagement
-@EnableConfigurationProperties
-@ServletComponentScan
-@MapperScan({"io.geekidea.springbootplus.**.mapper", "com.example.**.mapper"})
-@SpringBootApplication(scanBasePackages = {"io.geekidea.springbootplus", "com.example"})
+@SpringBootApplication
 public class SpringBootPlusApplication {
 
-    public static void main(String[] args) {
-        // Run spring-boot-plus
+    private static final String BACKSLASH = "/";
+
+    public static void main(String[] args) throws Exception {
         ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusApplication.class, args);
-        // Print Project Info
-        PrintApplicationInfo.print(context);
-        // Print Project Tip
-        PrintApplicationInfo.printTip(context);
+        // 打印项目信息
+        printlnProjectInfo(context);
+        System.out.println("  _____ _______       _____ _______    _____ _    _  _____ _____ ______  _____ _____ \n" +
+                " / ____|__   __|/\\   |  __ \\__   __|  / ____| |  | |/ ____/ ____|  ____|/ ____/ ____|\n" +
+                "| (___    | |  /  \\  | |__) | | |    | (___ | |  | | |   | |    | |__  | (___| (___  \n" +
+                " \\___ \\   | | / /\\ \\ |  _  /  | |     \\___ \\| |  | | |   | |    |  __|  \\___ \\\\___ \\ \n" +
+                " ____) |  | |/ ____ \\| | \\ \\  | |     ____) | |__| | |___| |____| |____ ____) |___) |\n" +
+                "|_____/   |_/_/    \\_\\_|  \\_\\ |_|    |_____/ \\____/ \\_____\\_____|______|_____/_____/ \n");
     }
-
 }
+
 ```
 
 ### 4. Access Swagger Docs
-[http://47.105.159.10:8888/api/swagger-ui.html](http://47.105.159.10:8888/api/swagger-ui.html)
-![swagger-ui.png](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/swagger-ui.png)
-![swagger-ui-1.png](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/swagger-ui-1.png)
+[http://localhost:8888/swagger-ui/index.html](http://localhost:8888/swagger-ui/index.html)
+![swagger-ui.png](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/swagger-v3.png)
 
 ### 5. Access Knife4j Docs 
-[http://47.105.159.10:8888/api/doc.html](http://47.105.159.10:8888/api/doc.html)
-![knife4j.png](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/knife4j.png)
-![knife4j-1.png](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/knife4j-1.png)
-
-
-## CentOS Quick Installation Environment / Build / Deploy / Launch Spring-boot-plus Project
-
-### 1. Download the installation script
-> Install `jdk`, `git`, `maven`, `redis`, `mysql`
-
-```bash
-wget -O download-install-all.sh https://springboot.plus/bin/download-install-all.sh
-```
+[http://localhost:8888/doc.html](http://localhost:8888/doc.html)
+![knife4j.png](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/knife4j-doc.png)
 
-### 2. Run the installation script
-```bash
-sh download-install-all.sh
-```
-
-### 3. Modify MySQL password
-```bash
-ALTER USER 'root'@'localhost' IDENTIFIED BY 'Springbootplus666!';
-exit
-mysql -uroot -pSpringbootplus666!
-```
 
-### 4. Import MySQL scripts
-```bash
-create database if not exists spring_boot_plus character set utf8mb4;
-use spring_boot_plus;
-source /root/mysql_spring_boot_plus.sql;
-show tables;
-exit
-```
-
-### 5. Download deployment script `deploy.sh`
-```bash
-wget -O deploy.sh https://springboot.plus/bin/deploy.sh
-```
-
-### 6. Execution script
-```bash
-sh deploy.sh
-```
-
-### 7. View project run log
-```bash
-tail -f -n 1000 /spring-boot-plus-server-2.0/logs/spring-boot-plus.log
-```
-
-
-## spring-boot-plus Views
-
-### spring-boot-plus IDEA Sources Views
-
-![spring-boot-plus-idea](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/idea.png)
-
-### [Spring Boot Admin Instances](http://47.105.159.10:8000/instances/11090f218c47/details)
-<p>
-    <a href="http://47.105.159.10:8000/instances/11090f218c47/details">
-        <img src="https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/springbootadmin-en.png" alt="spring-boot-admin instances">
-    </a>
-</p>
-
-### [Spring Boot Admin Statistics](http://47.105.159.10:8000/instances/11090f218c47/details)
-<p>
-    <a href="http://47.105.159.10:8000/instances/11090f218c47/details">
-        <img src="https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/springbootadmin-en-1.png" alt="spring-boot-admin statistics">
-    </a>
-</p>
-
-### [Spring Boot Admin Log](http://47.105.159.10:8000/instances/11090f218c47/logfile)
-<p>
-    <a href="http://47.105.159.10:8000/instances/11090f218c47/logfile">
-        <img src="https://springboot.plus/img/home/spring-boot-admin-log-en.png" alt="spring-boot-admin log">
-    </a>
-</p>
-
-## spring-boot-plus-vue Front-end Project
-### [GITHUB-REPO](https://github.com/geekidea/spring-boot-plus-vue)
-### [VUE WebSite](http://47.105.159.10/)
-#### VUE HOME
-![VUE HOME](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/springbootplusvue.png)
+## spring-boot-plus-vue Vue3 Project
+### [GITEE-REPO](https://gitee.com/geekidea/spring-boot-plus-vue3)
 #### System User List
-![System User List](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/springbootplusvue-1.png)
+![System User List](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/user-manager.png)
 #### System Role List
-![System Role List](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/springbootplusvue-2.png)
+![System Role List](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/role-manager.png)
 #### System Menu List
-![System Menu List](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/springbootplusvue-3.png)
+![System Menu List](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/menu-manager.png)
+#### System Department
+![System Department List](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/dept-manager-dark.png)
+#### System Log List
+![System Log List](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/log-manager.png)
+![System Log Detail](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/log-detail.png)
+#### User Profile
+![User Profile](https://geekidea.oss-cn-chengdu.aliyuncs.com/spring-boot-plus/img/profile.png)
 
 ## spring-boot-plus Videos  :movie_camera: 
-- [V2.x Video Introduction](https://www.bilibili.com/video/BV1HQ4y1M73i/)
-- [V1.x 5-Minutes-Finish-CRUD](https://www.bilibili.com/video/av67401204)
-- [CentOS Quick Installation JDK/Git/Maven/Redis/MySQL](https://www.bilibili.com/video/av67218836/)
-- [CentOS Quick Build / Deploy / Launch Spring-boot-plus Project](https://www.bilibili.com/video/av67218970/)
 
-## Contact
-QQ 625301326| Wechat geekideaio|  toutiao GeekIdea
--|-|-
-![spring-boot-plus QQ Group](https://spring-boot-plus.gitee.io/img/spring-boot-plus-qq-group.png) | ![Wechat Official Account](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/geekidea-wechat-official.jpg) | ![toutiao](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/geekidea-toutiao.jpeg) |
 
-## Donate
-Ask the author to drink coffee and let the code fly for a while! 
+QQ群 625301326| 微信公众号 geekideaio|
+-|-|
+![spring-boot-plus QQ Group](https://spring-boot-plus.gitee.io/img/spring-boot-plus-qq-group.png) | ![Wechat Official Account](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/geekidea-wechat-official.jpg) |
+
+微信技术交流群| 业务咨询商务合作|
+-|-|
+![微信群](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/wechat-group.png) | ![业务咨询商务合作](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/business-small.png) |
 
-![geekidea-wechat-donate](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/geekidea-wechat-donate.png)
 
 ## License
-spring-boot-plus is under the Apache 2.0 license. See the [LICENSE](https://github.com/geekidea/spring-boot-plus/blob/master/LICENSE) file for details.
+spring-boot-plus is under the MIT-License. See the [LICENSE](https://github.com/geekidea/spring-boot-plus/blob/master/LICENSE) file for details.
 
diff --git a/admin/.gitignore b/admin/.gitignore
deleted file mode 100644
index a2a3040a..00000000
--- a/admin/.gitignore
+++ /dev/null
@@ -1,31 +0,0 @@
-HELP.md
-target/
-!.mvn/wrapper/maven-wrapper.jar
-!**/src/main/**
-!**/src/test/**
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-
-### NetBeans ###
-/nbproject/private/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-build/
-
-### VS Code ###
-.vscode/
diff --git a/admin/README.md b/admin/README.md
deleted file mode 100644
index 78e8e7fa..00000000
--- a/admin/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Getting Started
-
-### Reference Documentation
-For further reference, please consider the following sections:
-
-* [Spring Security](https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#boot-features-security)
-* [Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#production-ready)
-* [Spring Boot Admin (Client)](https://codecentric.github.io/spring-boot-admin/current/#getting-started)
-* [Spring Boot Admin (Server)](https://codecentric.github.io/spring-boot-admin/current/#getting-started)
diff --git a/admin/pom.xml b/admin/pom.xml
deleted file mode 100644
index fca4af82..00000000
--- a/admin/pom.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>io.geekidea.springbootplus</groupId>
-        <artifactId>parent</artifactId>
-        <version>2.0</version>
-    </parent>
-
-    <artifactId>admin</artifactId>
-    <name>admin</name>
-    <description>Spring Boot Admin Server</description>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-web</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>de.codecentric</groupId>
-            <artifactId>spring-boot-admin-starter-server</artifactId>
-            <version>${spring-boot-admin.version}</version>
-            <exclusions>
-                <exclusion>
-                    <groupId>de.codecentric</groupId>
-                    <artifactId>spring-boot-admin-server-cloud</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-actuator</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-security</artifactId>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-maven-plugin</artifactId>
-                <version>${spring-boot.version}</version>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>repackage</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
-
-</project>
diff --git a/admin/src/main/java/io/geekidea/springbootplus/admin/SpringBootPlusAdminApplication.java b/admin/src/main/java/io/geekidea/springbootplus/admin/SpringBootPlusAdminApplication.java
deleted file mode 100644
index 00968679..00000000
--- a/admin/src/main/java/io/geekidea/springbootplus/admin/SpringBootPlusAdminApplication.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.admin;
-
-import de.codecentric.boot.admin.server.config.EnableAdminServer;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.core.env.ConfigurableEnvironment;
-
-/**
- * Spring Boot Admin 启动类
- *
- * @author geekidea
- * @date 2020/3/20
- **/
-@Slf4j
-@Configuration
-@EnableAutoConfiguration
-@EnableAdminServer
-@SpringBootApplication
-public class SpringBootPlusAdminApplication {
-
-    public static void main(String[] args) {
-        ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusAdminApplication.class, args);
-        ConfigurableEnvironment environment = context.getEnvironment();
-        String serverPort = environment.getProperty("server.port");
-        log.info("SpringBootAdmin: http://localhost:" + serverPort);
-    }
-
-}
diff --git a/admin/src/main/java/io/geekidea/springbootplus/admin/config/AdminWebMvcConfig.java b/admin/src/main/java/io/geekidea/springbootplus/admin/config/AdminWebMvcConfig.java
deleted file mode 100644
index 70112385..00000000
--- a/admin/src/main/java/io/geekidea/springbootplus/admin/config/AdminWebMvcConfig.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.admin.config;
-
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-/**
- * WebMvc配置
- *
- * @author geekidea
- * @date 2020/3/19
- */
-@Configuration
-public class AdminWebMvcConfig implements WebMvcConfigurer {
-
-    @Override
-    public void addResourceHandlers(ResourceHandlerRegistry registry) {
-        // 设置项目静态资源访问
-        registry.addResourceHandler("/static/**")
-                .addResourceLocations("classpath:/static/");
-    }
-
-}
diff --git a/admin/src/main/java/io/geekidea/springbootplus/admin/config/SecuritySecureConfig.java b/admin/src/main/java/io/geekidea/springbootplus/admin/config/SecuritySecureConfig.java
deleted file mode 100644
index 1d7df823..00000000
--- a/admin/src/main/java/io/geekidea/springbootplus/admin/config/SecuritySecureConfig.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.admin.config;
-
-import de.codecentric.boot.admin.server.config.AdminServerProperties;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.http.HttpMethod;
-import org.springframework.security.config.Customizer;
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
-import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
-
-import java.util.UUID;
-
-/**
- * Spring Boot Admin Security配置
- * https://codecentric.github.io/spring-boot-admin/current/#_securing_spring_boot_admin_server
- *
- * @author geekidea
- * @date 2020/3/20
- */
-@Configuration(proxyBeanMethods = false)
-public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
-
-    private final AdminServerProperties adminServer;
-
-    @Value("${spring-boot-plus.admin.username}")
-    private String username;
-
-    @Value("${spring-boot-plus.admin.password}")
-    private String password;
-
-    public SecuritySecureConfig(AdminServerProperties adminServer) {
-        this.adminServer = adminServer;
-    }
-
-    @Override
-    protected void configure(HttpSecurity http) throws Exception {
-        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
-        successHandler.setTargetUrlParameter("redirectTo");
-        successHandler.setDefaultTargetUrl(this.adminServer.path("/"));
-
-        http.authorizeRequests(
-                (authorizeRequests) -> authorizeRequests
-                        .antMatchers(this.adminServer.path("/assets/**")).permitAll()
-                        .antMatchers(this.adminServer.path("/static/**")).permitAll()
-                        .antMatchers(this.adminServer.path("/login")).permitAll()
-                        .anyRequest().authenticated()
-        ).formLogin(
-                (formLogin) -> formLogin.loginPage(this.adminServer.path("/login")).successHandler(successHandler).and()
-        ).logout((logout) -> logout.logoutUrl(this.adminServer.path("/logout"))).httpBasic(Customizer.withDefaults())
-                .csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
-                        .ignoringRequestMatchers(
-                                new AntPathRequestMatcher(this.adminServer.path("/instances"),
-                                        HttpMethod.POST.toString()),
-                                new AntPathRequestMatcher(this.adminServer.path("/instances/*"),
-                                        HttpMethod.DELETE.toString()),
-                                new AntPathRequestMatcher(this.adminServer.path("/actuator/**"))
-                        ))
-                .rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));
-    }
-
-    @Override
-    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-        auth.inMemoryAuthentication().withUser(username).password("{noop}" + password).roles("USER");
-    }
-
-}
\ No newline at end of file
diff --git a/admin/src/main/resources/application.yml b/admin/src/main/resources/application.yml
deleted file mode 100644
index b388188c..00000000
--- a/admin/src/main/resources/application.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-server:
-  port: 8000
-
-# Spring Boot Admin Server配置
-spring:
-  boot:
-    admin:
-      monitor:
-        period: 100000
-        status-lifetime: 100000
-        connect-timeout: 100000
-        read-timeout: 100000
-      ui:
-        external-views:
-          - label: "🚀"
-            url: https://springboot.plus
-            order: 2000
-
-# Spring Boot Admin 登录账号密码
-spring-boot-plus:
-  admin:
-    username: admin
-    password: admin
diff --git a/bootstrap/.gitignore b/bootstrap/.gitignore
deleted file mode 100644
index ccdf702f..00000000
--- a/bootstrap/.gitignore
+++ /dev/null
@@ -1,36 +0,0 @@
-/target/
-/classes
-!.mvn/wrapper/maven-wrapper.jar
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-
-### NetBeans ###
-/nbproject/private/
-/build/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-
-.DS_Store
-
-*.log
-logs
-
-*.rdb
-
-
-
diff --git a/bootstrap/README.md b/bootstrap/README.md
deleted file mode 100644
index 4e675991..00000000
--- a/bootstrap/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# bootstrap 项目启动模块
\ No newline at end of file
diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml
deleted file mode 100644
index 60b39a76..00000000
--- a/bootstrap/pom.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>io.geekidea.springbootplus</groupId>
-        <artifactId>parent</artifactId>
-        <version>2.0</version>
-    </parent>
-
-    <artifactId>bootstrap</artifactId>
-    <name>bootstrap</name>
-    <description>项目启动模块</description>
-
-    <dependencies>
-        <dependency>
-            <groupId>de.codecentric</groupId>
-            <artifactId>spring-boot-admin-starter-client</artifactId>
-            <version>${spring-boot-admin.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>io.geekidea.springbootplus</groupId>
-            <artifactId>framework</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>io.geekidea.springbootplus</groupId>
-            <artifactId>example</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>io.geekidea.springbootplus</groupId>
-            <artifactId>system</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>io.geekidea.springbootplus</groupId>
-            <artifactId>scheduled</artifactId>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-maven-plugin</artifactId>
-                <version>${spring-boot.version}</version>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>repackage</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
-
-
-</project>
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/SpringBootPlusApplication.java b/bootstrap/src/main/java/io/geekidea/springbootplus/SpringBootPlusApplication.java
deleted file mode 100644
index 82dda088..00000000
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/SpringBootPlusApplication.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus;
-
-import io.geekidea.springbootplus.framework.util.PrintApplicationInfo;
-import org.mybatis.spring.annotation.MapperScan;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.boot.web.servlet.ServletComponentScan;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.scheduling.annotation.EnableAsync;
-import org.springframework.scheduling.annotation.EnableScheduling;
-import org.springframework.transaction.annotation.EnableTransactionManagement;
-
-/**
- * spring-boot-plus 项目启动入口
- *
- * @author geekidea
- * @since 2018-11-08
- */
-@EnableAsync
-@EnableScheduling
-@EnableTransactionManagement
-@EnableConfigurationProperties
-@ServletComponentScan
-@MapperScan({"io.geekidea.springbootplus.**.mapper", "com.example.**.mapper"})
-@SpringBootApplication(scanBasePackages = {"io.geekidea.springbootplus", "com.example"})
-public class SpringBootPlusApplication {
-
-    public static void main(String[] args) {
-        // 启动spring-boot-plus
-        ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusApplication.class, args);
-        // 打印项目信息
-        PrintApplicationInfo.print(context);
-        // 打印项目提示
-        PrintApplicationInfo.printTip(context);
-    }
-
-}
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/aop/LogAop.java b/bootstrap/src/main/java/io/geekidea/springbootplus/aop/LogAop.java
deleted file mode 100644
index 31b6ab24..00000000
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/aop/LogAop.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.aop;
-
-import io.geekidea.springbootplus.framework.log.aop.BaseLogAop;
-import io.geekidea.springbootplus.framework.log.bean.OperationLogInfo;
-import io.geekidea.springbootplus.framework.log.bean.RequestInfo;
-import lombok.extern.slf4j.Slf4j;
-import org.aspectj.lang.JoinPoint;
-import org.aspectj.lang.ProceedingJoinPoint;
-import org.aspectj.lang.annotation.AfterThrowing;
-import org.aspectj.lang.annotation.Around;
-import org.aspectj.lang.annotation.Aspect;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.stereotype.Component;
-
-/**
- * <p>
- * Controller Aop
- * 获取响应结果信息
- * </p>
- *
- * @author geekidea
- * @date 2019-10-23
- */
-@Slf4j
-@Aspect
-@Component
-@ConditionalOnProperty(value = {"spring-boot-plus.aop.log.enable"}, matchIfMissing = true)
-public class LogAop extends BaseLogAop {
-
-    /**
-     * 切点
-     */
-    private static final String POINTCUT =
-            "execution(public * io.geekidea.springbootplus..*.controller..*.*(..)) || " +
-                    "execution(public * com.example..*.controller..*.*(..))";
-
-    @Around(POINTCUT)
-    @Override
-    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
-        return super.handle(joinPoint);
-    }
-
-    @AfterThrowing(pointcut = POINTCUT, throwing = "exception")
-    @Override
-    public void afterThrowing(JoinPoint joinPoint, Exception exception) {
-        super.handleAfterThrowing(exception);
-    }
-
-    @Override
-    protected void setRequestId(RequestInfo requestInfo) {
-        super.handleRequestId(requestInfo);
-    }
-
-    @Override
-    protected void getRequestInfo(RequestInfo requestInfo) {
-        // 处理请求参数日志
-        super.handleRequestInfo(requestInfo);
-    }
-
-    @Override
-    protected void getResponseResult(Object result) {
-        // 处理响应结果日志
-        super.handleResponseResult(result);
-    }
-
-    @Override
-    protected void finish(RequestInfo requestInfo, OperationLogInfo operationLogInfo, Object result, Exception exception) {
-        // 异步保存操作日志
-        super.saveSysOperationLog(requestInfo, operationLogInfo, result, exception);
-        // 异步保存登录日志
-        super.saveSysLoginLog(requestInfo, operationLogInfo, result, exception);
-    }
-
-}
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/config/JacksonConfig.java b/bootstrap/src/main/java/io/geekidea/springbootplus/config/JacksonConfig.java
deleted file mode 100644
index 9efd9775..00000000
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/config/JacksonConfig.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config;
-
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
-import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
-import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
-import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
-import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
-import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
-import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
-import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
-import io.geekidea.springbootplus.config.constant.DatePattern;
-import io.geekidea.springbootplus.framework.config.jackson.deserializer.JacksonDateDeserializer;
-import io.geekidea.springbootplus.framework.config.jackson.deserializer.JacksonDoubleDeserializer;
-import io.geekidea.springbootplus.framework.config.jackson.serializer.JacksonDateSerializer;
-import io.geekidea.springbootplus.framework.config.jackson.serializer.JacksonIntegerDeserializer;
-import io.geekidea.springbootplus.framework.core.xss.XssJacksonDeserializer;
-import io.geekidea.springbootplus.framework.core.xss.XssJacksonSerializer;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.http.converter.HttpMessageConverter;
-import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
-import java.time.format.DateTimeFormatter;
-import java.util.Date;
-import java.util.List;
-
-/**
- * @author geekidea
- * @author 2018-11-08
- */
-@Configuration
-public class JacksonConfig implements WebMvcConfigurer {
-
-    @Value("${spring-boot-plus.filter.xss.enable}")
-    private boolean enableXss;
-
-    @Override
-    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
-        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
-
-        ObjectMapper objectMapper = jackson2HttpMessageConverter.getObjectMapper();
-        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
-        objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
-
-        SimpleModule simpleModule = new SimpleModule();
-        // Long类型序列化成字符串，避免Long精度丢失
-//        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
-//        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
-
-        // XSS序列化
-        if (enableXss){
-            simpleModule.addSerializer(String.class, new XssJacksonSerializer());
-            simpleModule.addDeserializer(String.class, new XssJacksonDeserializer());
-        }
-
-        // Date
-        simpleModule.addSerializer(Date.class, new JacksonDateSerializer());
-        simpleModule.addDeserializer(Date.class, new JacksonDateDeserializer());
-
-        simpleModule.addDeserializer(Integer.class, new JacksonIntegerDeserializer());
-        simpleModule.addDeserializer(Double.class, new JacksonDoubleDeserializer());
-
-        // jdk8日期序列化和反序列化设置
-        JavaTimeModule javaTimeModule = new JavaTimeModule();
-        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.YYYY_MM_DD_HH_MM_SS)));
-        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.YYYY_MM_DD_HH_MM_SS)));
-
-        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DatePattern.YYYY_MM_DD)));
-        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DatePattern.YYYY_MM_DD)));
-
-        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.HH_MM_SS)));
-        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.HH_MM_SS)));
-
-        objectMapper.registerModule(simpleModule).registerModule(javaTimeModule).registerModule(new ParameterNamesModule());
-
-        jackson2HttpMessageConverter.setObjectMapper(objectMapper);
-
-        //放到第一个
-        converters.add(0, jackson2HttpMessageConverter);
-    }
-}
\ No newline at end of file
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/config/MybatisPlusConfig.java b/bootstrap/src/main/java/io/geekidea/springbootplus/config/MybatisPlusConfig.java
deleted file mode 100644
index fe7f3d55..00000000
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/config/MybatisPlusConfig.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config;
-
-import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
-import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * <p>
- * MybatisPlus配置
- * </p>
- *
- * @author geekidea
- * @since 2018-11-08
- */
-@Configuration
-public class MybatisPlusConfig {
-
-    /**
-     * mybatis-plus分页插件
-     */
-    @Bean
-    public PaginationInterceptor paginationInterceptor() {
-        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
-        return paginationInterceptor;
-    }
-
-    /**
-     * mybatios-plus乐观锁插件
-     *
-     * @return
-     */
-    @Bean
-    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
-        return new OptimisticLockerInterceptor();
-    }
-
-}
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/config/RedisCacheConfig.java b/bootstrap/src/main/java/io/geekidea/springbootplus/config/RedisCacheConfig.java
deleted file mode 100644
index 2a1e8ffb..00000000
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/config/RedisCacheConfig.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config;
-
-import org.springframework.cache.CacheManager;
-import org.springframework.cache.annotation.CachingConfigurerSupport;
-import org.springframework.cache.annotation.EnableCaching;
-import org.springframework.cache.interceptor.KeyGenerator;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.data.redis.cache.RedisCacheConfiguration;
-import org.springframework.data.redis.cache.RedisCacheManager;
-import org.springframework.data.redis.connection.RedisConnectionFactory;
-
-import java.time.Duration;
-import java.util.*;
-
-/**
- * <p>
- *     Redis Cache配置
- * </p>
- * @author geekidea
- * @date 2018-11-08
- */
-@Configuration
-@EnableCaching
-public class RedisCacheConfig extends CachingConfigurerSupport {
-
-    @Bean
-    @Override
-    public KeyGenerator keyGenerator() {
-        return (target, method, params) -> {
-            StringBuffer redisKey = new StringBuffer();
-            redisKey.append(target.getClass().getName()).append("-");
-            redisKey.append(method.getName());
-            if (params.length > 0) {
-                redisKey.append("-").append(Arrays.deepToString(params));
-            }
-            return redisKey.toString();
-        };
-    }
-
-
-    @Bean
-    public CacheManager cacheManager(RedisConnectionFactory factory) {
-        // 生成一个默认配置，通过config对象即可对缓存进行自定义配置
-        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
-        // 设置缓存的默认过期时间，也是使用Duration设置
-        // 过期时间5分钟
-        config = config.entryTtl(Duration.ofMinutes(5));
-
-        // 设置一个初始化的缓存空间set集合
-        Set<String> cacheNames =  new HashSet<>();
-        cacheNames.add("my-redis-cache1");
-        cacheNames.add("my-redis-cache2");
-
-        // 对每个缓存空间应用不同的配置
-        Map<String, RedisCacheConfiguration> configMap = new HashMap<>(10);
-        configMap.put("my-redis-cache1", config);
-        configMap.put("my-redis-cache2", config.entryTtl(Duration.ofSeconds(120)));
-
-        // 使用自定义的缓存配置初始化一个cacheManager
-        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
-                // 注意这两句的调用顺序，一定要先调用该方法设置初始化的缓存名，再初始化相关的配置
-                .initialCacheNames(cacheNames)
-                .withInitialCacheConfigurations(configMap)
-                .build();
-
-        return cacheManager;
-    }
-
-}
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/config/RestTemplateConfig.java b/bootstrap/src/main/java/io/geekidea/springbootplus/config/RestTemplateConfig.java
deleted file mode 100644
index baf2b99f..00000000
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/config/RestTemplateConfig.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.http.client.ClientHttpRequestFactory;
-import org.springframework.http.client.SimpleClientHttpRequestFactory;
-import org.springframework.web.client.RestTemplate;
-
-/**
- * @author geekidea
- * @date 2018-11-08
- */
-@Configuration
-public class RestTemplateConfig {
-
-    @Bean
-    public RestTemplate restTemplate(ClientHttpRequestFactory factory){
-        return new RestTemplate(factory);
-    }
-
-    @Bean
-    public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
-        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
-        //单位为ms
-        factory.setReadTimeout(5000);
-        //单位为ms
-        factory.setConnectTimeout(5000);
-        return factory;
-    }
-}
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/config/ShiroConfig.java b/bootstrap/src/main/java/io/geekidea/springbootplus/config/ShiroConfig.java
deleted file mode 100644
index e3598f5a..00000000
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/config/ShiroConfig.java
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config;
-
-import com.alibaba.fastjson.JSON;
-import io.geekidea.springbootplus.config.properties.JwtProperties;
-import io.geekidea.springbootplus.config.properties.ShiroPermissionProperties;
-import io.geekidea.springbootplus.config.properties.ShiroProperties;
-import io.geekidea.springbootplus.framework.shiro.cache.LoginRedisService;
-import io.geekidea.springbootplus.framework.shiro.exception.ShiroConfigException;
-import io.geekidea.springbootplus.framework.shiro.jwt.JwtCredentialsMatcher;
-import io.geekidea.springbootplus.framework.shiro.jwt.JwtFilter;
-import io.geekidea.springbootplus.framework.shiro.jwt.JwtRealm;
-import io.geekidea.springbootplus.framework.shiro.service.ShiroLoginService;
-import io.geekidea.springbootplus.framework.util.IniUtil;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.collections.MapUtils;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.shiro.SecurityUtils;
-import org.apache.shiro.authc.Authenticator;
-import org.apache.shiro.authc.credential.CredentialsMatcher;
-import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
-import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
-import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
-import org.apache.shiro.mgt.DefaultSubjectDAO;
-import org.apache.shiro.mgt.SecurityManager;
-import org.apache.shiro.mgt.SessionStorageEvaluator;
-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.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-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;
-import javax.servlet.Filter;
-import java.util.*;
-
-/**
- * Shiro配置
- * https://shiro.apache.org/spring.html
- * https://shiro.apache.org/spring-boot.html
- *
- * @author geekidea
- * @date 2019-09-27
- * @since 1.3.0.RELEASE
- **/
-@Slf4j
-@Configuration
-@EnableConfigurationProperties({ShiroProperties.class})
-@ConditionalOnProperty(value = {"spring-boot-plus.shiro.enable"}, matchIfMissing = true)
-public class ShiroConfig {
-
-    /**
-     * JWT过滤器名称
-     */
-    private static final String JWT_FILTER_NAME = "jwtFilter";
-
-    /**
-     * Shiro过滤器名称
-     */
-    private static final String SHIRO_FILTER_NAME = "shiroFilter";
-
-    /**
-     * anon
-     */
-    private static final String ANON = "anon";
-
-
-    @Bean
-    public CredentialsMatcher credentialsMatcher() {
-        return new JwtCredentialsMatcher();
-    }
-
-    /**
-     * JWT数据源验证
-     *
-     * @return
-     */
-    @Bean
-    public JwtRealm jwtRealm(LoginRedisService loginRedisService) {
-        JwtRealm jwtRealm = new JwtRealm(loginRedisService);
-        jwtRealm.setCachingEnabled(false);
-        jwtRealm.setCredentialsMatcher(credentialsMatcher());
-        return jwtRealm;
-    }
-
-
-    @Bean
-    public SessionStorageEvaluator sessionStorageEvaluator() {
-        DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator();
-        sessionStorageEvaluator.setSessionStorageEnabled(false);
-        return sessionStorageEvaluator;
-    }
-
-    @Bean
-    public DefaultSubjectDAO subjectDAO() {
-        DefaultSubjectDAO defaultSubjectDAO = new DefaultSubjectDAO();
-        defaultSubjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator());
-        return defaultSubjectDAO;
-    }
-
-    /**
-     * 安全管理器配置
-     *
-     * @return
-     */
-    @Bean
-    public SecurityManager securityManager(LoginRedisService loginRedisService) {
-        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
-        securityManager.setRealm(jwtRealm(loginRedisService));
-        securityManager.setSubjectDAO(subjectDAO());
-        SecurityUtils.setSecurityManager(securityManager);
-        return securityManager;
-    }
-
-    /**
-     * ShiroFilterFactoryBean配置
-     *
-     * @param securityManager
-     * @param loginRedisService
-     * @param shiroProperties
-     * @param jwtProperties
-     * @return
-     */
-    @Bean(SHIRO_FILTER_NAME)
-    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager,
-                                                         ShiroLoginService shiroLoginService,
-                                                         LoginRedisService loginRedisService,
-                                                         ShiroProperties shiroProperties,
-                                                         JwtProperties jwtProperties) {
-        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
-        shiroFilterFactoryBean.setSecurityManager(securityManager);
-        Map<String, Filter> filterMap = getFilterMap(shiroLoginService, loginRedisService, jwtProperties);
-        shiroFilterFactoryBean.setFilters(filterMap);
-        Map<String, String> filterChainMap = getFilterChainDefinitionMap(shiroProperties);
-        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
-        return shiroFilterFactoryBean;
-    }
-
-
-    /**
-     * 获取filter map
-     *
-     * @return
-     */
-    private Map<String, Filter> getFilterMap(ShiroLoginService shiroLoginService,
-                                             LoginRedisService loginRedisService,
-                                             JwtProperties jwtProperties) {
-        Map<String, Filter> filterMap = new LinkedHashMap<>();
-        filterMap.put(JWT_FILTER_NAME, new JwtFilter(shiroLoginService, loginRedisService, jwtProperties));
-        return filterMap;
-    }
-
-
-    /**
-     * Shiro路径权限配置
-     *
-     * @return
-     */
-    private Map<String, String> getFilterChainDefinitionMap(ShiroProperties shiroProperties) {
-        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
-        // 获取排除的路径
-        List<String[]> anonList = shiroProperties.getAnon();
-        log.debug("anonList:{}", JSON.toJSONString(anonList));
-        if (CollectionUtils.isNotEmpty(anonList)) {
-            anonList.forEach(anonArray -> {
-                if (ArrayUtils.isNotEmpty(anonArray)) {
-                    for (String anonPath : anonArray) {
-                        filterChainDefinitionMap.put(anonPath, ANON);
-                    }
-                }
-            });
-        }
-
-        // 获取ini格式配置
-        String definitions = shiroProperties.getFilterChainDefinitions();
-        if (StringUtils.isNotBlank(definitions)) {
-            Map<String, String> section = IniUtil.parseIni(definitions);
-            log.debug("definitions:{}", JSON.toJSONString(section));
-            for (Map.Entry<String, String> entry : section.entrySet()) {
-                filterChainDefinitionMap.put(entry.getKey(), entry.getValue());
-            }
-        }
-
-        // 获取自定义权限路径配置集合
-        List<ShiroPermissionProperties> permissionConfigs = shiroProperties.getPermission();
-        log.debug("permissionConfigs:{}", JSON.toJSONString(permissionConfigs));
-        if (CollectionUtils.isNotEmpty(permissionConfigs)) {
-            for (ShiroPermissionProperties permissionConfig : permissionConfigs) {
-                String url = permissionConfig.getUrl();
-                String[] urls = permissionConfig.getUrls();
-                String permission = permissionConfig.getPermission();
-                if (StringUtils.isBlank(url) && ArrayUtils.isEmpty(urls)) {
-                    throw new ShiroConfigException("shiro permission config 路径配置不能为空");
-                }
-                if (StringUtils.isBlank(permission)) {
-                    throw new ShiroConfigException("shiro permission config permission不能为空");
-                }
-
-                if (StringUtils.isNotBlank(url)) {
-                    filterChainDefinitionMap.put(url, permission);
-                }
-                if (ArrayUtils.isNotEmpty(urls)) {
-                    for (String string : urls) {
-                        filterChainDefinitionMap.put(string, permission);
-                    }
-                }
-            }
-        }
-
-        // 如果启用shiro，则设置最后一个设置为JWTFilter，否则全部路径放行
-        if (shiroProperties.isEnable()) {
-            filterChainDefinitionMap.put("/**", JWT_FILTER_NAME);
-        } else {
-            filterChainDefinitionMap.put("/**", ANON);
-        }
-
-        log.debug("filterChainMap:{}", JSON.toJSONString(filterChainDefinitionMap));
-
-        // 添加默认的filter
-        Map<String, String> newFilterChainDefinitionMap = addDefaultFilterDefinition(filterChainDefinitionMap);
-        return newFilterChainDefinitionMap;
-    }
-
-    /**
-     * 添加默认的filter权限过滤
-     *
-     * @param filterChainDefinitionMap
-     * @return
-     */
-    private Map<String, String> addDefaultFilterDefinition(Map<String, String> filterChainDefinitionMap) {
-        if (MapUtils.isEmpty(filterChainDefinitionMap)) {
-            return filterChainDefinitionMap;
-        }
-        final Map<String, String> map = new LinkedHashMap<>();
-        for (Map.Entry<String, String> entry : filterChainDefinitionMap.entrySet()) {
-            String key = entry.getKey();
-            String value = entry.getValue();
-            String definition;
-            String[] strings = value.split(",");
-            List<String> list = new ArrayList<>();
-            list.addAll(Arrays.asList(strings));
-            definition = String.join(",", list);
-            map.put(key, definition);
-        }
-        return map;
-    }
-
-    /**
-     * ShiroFilter配置
-     *
-     * @return
-     */
-    @Bean
-    public FilterRegistrationBean delegatingFilterProxy() {
-        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
-        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
-        proxy.setTargetFilterLifecycle(true);
-        proxy.setTargetBeanName(SHIRO_FILTER_NAME);
-        filterRegistrationBean.setFilter(proxy);
-        filterRegistrationBean.setAsyncSupported(true);
-        filterRegistrationBean.setEnabled(true);
-        filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
-        return filterRegistrationBean;
-    }
-
-    @Bean
-    public Authenticator authenticator(LoginRedisService loginRedisService) {
-        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
-        authenticator.setRealms(Arrays.asList(jwtRealm(loginRedisService)));
-        authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
-        return authenticator;
-    }
-
-
-    /**
-     * Enabling Shiro Annotations
-     *
-     * @return
-     */
-    @Bean
-    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
-        return new LifecycleBeanPostProcessor();
-    }
-
-    @Bean
-    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
-        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
-        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
-        return authorizationAttributeSourceAdvisor;
-    }
-
-}
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/config/SpringBootPlusConfig.java b/bootstrap/src/main/java/io/geekidea/springbootplus/config/SpringBootPlusConfig.java
deleted file mode 100644
index 4cfd1008..00000000
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/config/SpringBootPlusConfig.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-package io.geekidea.springbootplus.config;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * spring-boot-plus配置
- *
- * @author geekidea
- * @date 2019/8/4
- * @since 1.2.0-RELEASE
- */
-@Slf4j
-public class SpringBootPlusConfig {
-
-}
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/config/SpringBootPlusCorsConfig.java b/bootstrap/src/main/java/io/geekidea/springbootplus/config/SpringBootPlusCorsConfig.java
deleted file mode 100644
index 077f5121..00000000
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/config/SpringBootPlusCorsConfig.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config;
-
-
-import io.geekidea.springbootplus.config.properties.SpringBootPlusCorsProperties;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.web.servlet.FilterRegistrationBean;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.core.Ordered;
-import org.springframework.web.cors.CorsConfiguration;
-import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-import org.springframework.web.filter.CorsFilter;
-
-/**
- * 跨域配置
- *
- * @author geekidea
- * @date 2019/10/14
- */
-@Slf4j
-@Configuration
-@ConditionalOnProperty(value = {"spring-boot-plus.cors.enable"}, matchIfMissing = true)
-public class SpringBootPlusCorsConfig {
-
-    /**
-     * CORS跨域设置
-     *
-     * @return
-     */
-    @Bean
-    public FilterRegistrationBean corsFilter(SpringBootPlusCorsProperties corsProperties) {
-        log.debug("corsProperties:{}", corsProperties);
-        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
-        CorsConfiguration corsConfiguration = new CorsConfiguration();
-        // 跨域配置
-        corsConfiguration.setAllowedOrigins(corsProperties.getAllowedOrigins());
-        corsConfiguration.setAllowedHeaders(corsProperties.getAllowedHeaders());
-        corsConfiguration.setAllowedMethods(corsProperties.getAllowedMethods());
-        corsConfiguration.setAllowCredentials(corsProperties.isAllowCredentials());
-        corsConfiguration.setExposedHeaders(corsProperties.getExposedHeaders());
-        corsConfiguration.setMaxAge(corsConfiguration.getMaxAge());
-
-        source.registerCorsConfiguration(corsProperties.getPath(), corsConfiguration);
-        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
-        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
-        bean.setEnabled(corsProperties.isEnable());
-        return bean;
-    }
-
-}
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/config/SpringBootPlusWebMvcConfig.java b/bootstrap/src/main/java/io/geekidea/springbootplus/config/SpringBootPlusWebMvcConfig.java
deleted file mode 100644
index 67498454..00000000
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/config/SpringBootPlusWebMvcConfig.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config;
-
-import com.alibaba.fastjson.JSON;
-import io.geekidea.springbootplus.config.properties.SpringBootPlusFilterProperties;
-import io.geekidea.springbootplus.config.properties.SpringBootPlusInterceptorProperties;
-import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties;
-import io.geekidea.springbootplus.framework.core.filter.RequestDetailFilter;
-import io.geekidea.springbootplus.framework.core.interceptor.PermissionInterceptor;
-import io.geekidea.springbootplus.framework.core.xss.XssFilter;
-import io.geekidea.springbootplus.framework.util.IniUtil;
-import io.geekidea.springbootplus.system.interceptor.DownloadInterceptor;
-import io.geekidea.springbootplus.system.interceptor.ResourceInterceptor;
-import io.geekidea.springbootplus.system.interceptor.UploadInterceptor;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.web.servlet.FilterRegistrationBean;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
-import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-import javax.annotation.PostConstruct;
-import java.util.Map;
-
-/**
- * WebMvc配置
- *
- * @author geekidea
- * @date 2018-11-08
- */
-@Slf4j
-@Configuration
-public class SpringBootPlusWebMvcConfig implements WebMvcConfigurer {
-
-    /**
-     * spring-boot-plus配置属性
-     */
-    @Autowired
-    private SpringBootPlusProperties springBootPlusProperties;
-
-    /**
-     * Filter配置
-     */
-    private SpringBootPlusFilterProperties filterConfig;
-
-    /**
-     * 拦截器配置
-     */
-    private SpringBootPlusInterceptorProperties interceptorConfig;
-
-    /**
-     * RequestDetailFilter配置
-     *
-     * @return
-     */
-    @Bean
-    public FilterRegistrationBean requestDetailFilter() {
-        SpringBootPlusFilterProperties.FilterConfig requestFilterConfig = filterConfig.getRequest();
-        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
-        filterRegistrationBean.setFilter(new RequestDetailFilter());
-        filterRegistrationBean.setEnabled(requestFilterConfig.isEnable());
-        filterRegistrationBean.addUrlPatterns(requestFilterConfig.getUrlPatterns());
-        filterRegistrationBean.setOrder(requestFilterConfig.getOrder());
-        filterRegistrationBean.setAsyncSupported(requestFilterConfig.isAsync());
-        return filterRegistrationBean;
-    }
-
-    /**
-     * XssFilter配置
-     *
-     * @return
-     */
-    @Bean
-    public FilterRegistrationBean xssFilter() {
-        SpringBootPlusFilterProperties.FilterConfig xssFilterConfig = filterConfig.getXss();
-        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
-        filterRegistrationBean.setFilter(new XssFilter());
-        filterRegistrationBean.setEnabled(xssFilterConfig.isEnable());
-        filterRegistrationBean.addUrlPatterns(xssFilterConfig.getUrlPatterns());
-        filterRegistrationBean.setOrder(xssFilterConfig.getOrder());
-        filterRegistrationBean.setAsyncSupported(xssFilterConfig.isAsync());
-        return filterRegistrationBean;
-    }
-
-
-    /**
-     * 自定义权限拦截器
-     *
-     * @return
-     */
-    @Bean
-    public PermissionInterceptor permissionInterceptor() {
-        return new PermissionInterceptor();
-    }
-
-    /**
-     * 上传拦截器
-     *
-     * @return
-     */
-    @Bean
-    public UploadInterceptor uploadInterceptor() {
-        return new UploadInterceptor();
-    }
-
-    /**
-     * 资源拦截器
-     *
-     * @return
-     */
-    @Bean
-    public ResourceInterceptor resourceInterceptor() {
-        return new ResourceInterceptor();
-    }
-
-    /**
-     * 下载拦截器
-     *
-     * @return
-     */
-    @Bean
-    public DownloadInterceptor downloadInterceptor() {
-        return new DownloadInterceptor();
-    }
-
-
-    @PostConstruct
-    public void init() {
-        filterConfig = springBootPlusProperties.getFilter();
-        interceptorConfig = springBootPlusProperties.getInterceptor();
-        // 打印SpringBootPlusProperties配置信息
-        log.debug("SpringBootPlusProperties：{}", JSON.toJSONString(springBootPlusProperties));
-    }
-
-    @Override
-    public void addInterceptors(InterceptorRegistry registry) {
-        // 上传拦截器
-        if (interceptorConfig.getUpload().isEnable()) {
-            registry.addInterceptor(uploadInterceptor())
-                    .addPathPatterns(interceptorConfig.getUpload().getIncludePaths());
-        }
-
-        // 资源拦截器注册
-        if (interceptorConfig.getResource().isEnable()) {
-            registry.addInterceptor(resourceInterceptor())
-                    .addPathPatterns(interceptorConfig.getResource().getIncludePaths());
-        }
-
-        // 下载拦截器注册
-        if (interceptorConfig.getDownload().isEnable()) {
-            registry.addInterceptor(downloadInterceptor())
-                    .addPathPatterns(interceptorConfig.getDownload().getIncludePaths());
-        }
-
-        // 自定义权限拦截器注册
-        if (interceptorConfig.getPermission().isEnable()) {
-            registry.addInterceptor(permissionInterceptor())
-                    .addPathPatterns(interceptorConfig.getPermission().getIncludePaths())
-                    .excludePathPatterns(interceptorConfig.getPermission().getExcludePaths());
-        }
-
-    }
-
-    @Override
-    public void addResourceHandlers(ResourceHandlerRegistry registry) {
-        // 设置项目静态资源访问
-        String resourceHandlers = springBootPlusProperties.getResourceHandlers();
-        if (StringUtils.isNotBlank(resourceHandlers)) {
-            Map<String, String> map = IniUtil.parseIni(resourceHandlers);
-            for (Map.Entry<String, String> entry : map.entrySet()) {
-                String pathPatterns = entry.getKey();
-                String resourceLocations = entry.getValue();
-                registry.addResourceHandler(pathPatterns)
-                        .addResourceLocations(resourceLocations);
-            }
-        }
-
-        // 设置上传文件访问路径
-        registry.addResourceHandler(springBootPlusProperties.getResourceAccessPatterns())
-                .addResourceLocations("file:" + springBootPlusProperties.getUploadPath());
-    }
-
-}
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/config/Swagger2Config.java b/bootstrap/src/main/java/io/geekidea/springbootplus/config/Swagger2Config.java
deleted file mode 100644
index ac027f3c..00000000
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/config/Swagger2Config.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config;
-
-import com.fasterxml.jackson.databind.introspect.AnnotatedField;
-import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
-import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import io.geekidea.springbootplus.config.properties.SwaggerProperties;
-import io.geekidea.springbootplus.framework.common.exception.SpringBootPlusConfigException;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Import;
-import org.springframework.stereotype.Component;
-import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
-import springfox.documentation.RequestHandler;
-import springfox.documentation.annotations.ApiIgnore;
-import springfox.documentation.builders.ApiInfoBuilder;
-import springfox.documentation.builders.ParameterBuilder;
-import springfox.documentation.builders.PathSelectors;
-import springfox.documentation.builders.RequestHandlerSelectors;
-import springfox.documentation.schema.ModelRef;
-import springfox.documentation.service.ApiInfo;
-import springfox.documentation.service.Contact;
-import springfox.documentation.service.Parameter;
-import springfox.documentation.spi.DocumentationType;
-import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
-import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
-import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
-import springfox.documentation.spring.web.plugins.Docket;
-import springfox.documentation.swagger.common.SwaggerPluginSupport;
-import springfox.documentation.swagger2.annotations.EnableSwagger2;
-
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import static springfox.documentation.schema.Annotations.findPropertyAnnotation;
-import static springfox.documentation.swagger.schema.ApiModelProperties.findApiModePropertyAnnotation;
-
-/**
- * Swagger2全局配置
- *
- * @author geekidea
- * @date 2020/3/21
- */
-@Slf4j
-@Configuration
-@EnableSwagger2
-@EnableKnife4j
-@Import(BeanValidatorPluginsConfiguration.class)
-@ConditionalOnProperty(value = {"knife4j.enable"}, matchIfMissing = true)
-public class Swagger2Config {
-
-    @Autowired
-    private SwaggerProperties swaggerProperties;
-
-    /**
-     * 扫描多包时，包路径的拆分符,分号
-     */
-    private static final String SPLIT_COMMA = ",";
-
-    /**
-     * 扫描多包时，包路径的拆分符,逗号
-     */
-    private static final String SPLIT_SEMICOLON = ";";
-
-    /**
-     * Swagger忽略的参数类型
-     */
-    private Class<?>[] ignoredParameterTypes = new Class[]{
-            ServletRequest.class,
-            ServletResponse.class,
-            HttpServletRequest.class,
-            HttpServletResponse.class,
-            HttpSession.class,
-            ApiIgnore.class
-    };
-
-    @Bean
-    public Docket createRestApi() {
-        // 获取需要扫描的包
-        String[] basePackages = getBasePackages();
-        ApiSelectorBuilder apiSelectorBuilder = new Docket(DocumentationType.SWAGGER_2)
-                .apiInfo(apiInfo())
-                .select();
-        // 如果扫描的包为空，则默认扫描类上有@Api注解的类
-        if (ArrayUtils.isEmpty(basePackages)) {
-            apiSelectorBuilder.apis(RequestHandlerSelectors.withClassAnnotation(Api.class));
-        } else {
-            // 扫描指定的包
-            apiSelectorBuilder.apis(basePackage(basePackages));
-        }
-        Docket docket = apiSelectorBuilder.paths(PathSelectors.any())
-                .build()
-                .enable(swaggerProperties.isEnable())
-                .ignoredParameterTypes(ignoredParameterTypes)
-                .globalOperationParameters(getParameters());
-        return docket;
-    }
-
-    /**
-     * 获取apiInfo
-     *
-     * @return
-     */
-    private ApiInfo apiInfo() {
-        return new ApiInfoBuilder()
-                .title(swaggerProperties.getTitle())
-                .description(swaggerProperties.getDescription())
-                .termsOfServiceUrl(swaggerProperties.getUrl())
-                .contact(new Contact(swaggerProperties.getContactName(), swaggerProperties.getContactUrl(), swaggerProperties.getContactEmail()))
-                .version(swaggerProperties.getVersion())
-                .build();
-    }
-
-    /**
-     * 获取扫描的包
-     *
-     * @return
-     */
-    public String[] getBasePackages() {
-        log.debug("swaggerProperties = " + swaggerProperties);
-        String basePackage = swaggerProperties.getBasePackage();
-        if (StringUtils.isBlank(basePackage)) {
-            throw new SpringBootPlusConfigException("Swagger basePackage不能为空");
-        }
-        String[] basePackages = null;
-        if (basePackage.contains(SPLIT_COMMA)) {
-            basePackages = basePackage.split(SPLIT_COMMA);
-        } else if (basePackage.contains(SPLIT_SEMICOLON)) {
-            basePackages = basePackage.split(SPLIT_SEMICOLON);
-        }
-        log.info("swagger scan basePackages:" + Arrays.toString(basePackages));
-        return basePackages;
-    }
-
-    /**
-     * 添加额外参数
-     *
-     * @return
-     */
-    private List<Parameter> getParameters() {
-        // 获取自定义参数配置
-        List<SwaggerProperties.ParameterConfig> parameterConfig = swaggerProperties.getParameterConfig();
-        if (CollectionUtils.isEmpty(parameterConfig)) {
-            return null;
-        }
-        List<Parameter> parameters = new ArrayList<>();
-        parameterConfig.forEach(parameter -> {
-            // 设置自定义参数
-            parameters.add(new ParameterBuilder()
-                    .name(parameter.getName())
-                    .description(parameter.getDescription())
-                    .modelRef(new ModelRef(parameter.getDataType()))
-                    .parameterType(parameter.getType())
-                    .required(parameter.isRequired())
-                    .defaultValue(parameter.getDefaultValue())
-                    .build());
-        });
-        return parameters;
-    }
-
-
-    public static Predicate<RequestHandler> basePackage(final String[] basePackages) {
-        return input -> declaringClass(input).transform(handlerPackage(basePackages)).or(true);
-    }
-
-    private static Function<Class<?>, Boolean> handlerPackage(final String[] basePackages) {
-        return input -> {
-            // 循环判断匹配
-            for (String strPackage : basePackages) {
-                boolean isMatch = input.getPackage().getName().startsWith(strPackage);
-                if (isMatch) {
-                    return true;
-                }
-            }
-            return false;
-        };
-    }
-
-    @SuppressWarnings("deprecation")
-    private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
-        return Optional.fromNullable(input.declaringClass());
-    }
-
-    /**
-     * 按照类中字段顺序显示
-     */
-    @Component
-    public static class ApiModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin {
-
-        @Override
-        public void apply(ModelPropertyContext context) {
-            try {
-                Optional<BeanPropertyDefinition> beanPropertyDefinitionOptional = context.getBeanPropertyDefinition();
-                Optional<ApiModelProperty> annotation = Optional.absent();
-                if (context.getAnnotatedElement().isPresent()) {
-                    annotation = annotation.or(findApiModePropertyAnnotation(context.getAnnotatedElement().get()));
-                }
-                if (context.getBeanPropertyDefinition().isPresent()) {
-                    annotation = annotation.or(findPropertyAnnotation(context.getBeanPropertyDefinition().get(), ApiModelProperty.class));
-                }
-                if (beanPropertyDefinitionOptional.isPresent()) {
-                    BeanPropertyDefinition beanPropertyDefinition = beanPropertyDefinitionOptional.get();
-                    if (annotation.isPresent() && annotation.get().position() != 0) {
-                        return;
-                    }
-                    AnnotatedField annotatedField = beanPropertyDefinition.getField();
-                    if (annotatedField == null) {
-                        return;
-                    }
-                    Class<?> clazz = annotatedField.getDeclaringClass();
-                    Field[] fields = clazz.getDeclaredFields();
-                    // 获取当前字段对象
-                    Field field = clazz.getDeclaredField(annotatedField.getName());
-                    boolean required = false;
-                    // 获取字段注解
-                    NotNull notNull = field.getDeclaredAnnotation(NotNull.class);
-                    NotBlank notBlank = field.getDeclaredAnnotation(NotBlank.class);
-                    if (notNull != null || notBlank != null) {
-                        required = true;
-                    }
-                    int position = ArrayUtils.indexOf(fields, field);
-                    if (position != -1) {
-                        context.getBuilder().position(position).required(required);
-                    }
-                }
-            } catch (Exception exception) {
-                log.error("Swagger ApiModelProperty预处理异常", exception);
-            }
-        }
-
-        @Override
-        public boolean supports(DocumentationType delimiter) {
-            return SwaggerPluginSupport.pluginDoesApply(delimiter);
-        }
-    }
-
-}
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/handler/GlobalExceptionHandler.java b/bootstrap/src/main/java/io/geekidea/springbootplus/handler/GlobalExceptionHandler.java
deleted file mode 100644
index 9c8828d2..00000000
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/handler/GlobalExceptionHandler.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.handler;
-
-import com.alibaba.fastjson.JSON;
-import com.auth0.jwt.exceptions.JWTDecodeException;
-import io.geekidea.springbootplus.framework.common.api.ApiCode;
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.common.exception.BusinessException;
-import io.geekidea.springbootplus.framework.common.exception.DaoException;
-import io.geekidea.springbootplus.framework.common.exception.SpringBootPlusException;
-import io.geekidea.springbootplus.framework.core.bean.RequestDetail;
-import io.geekidea.springbootplus.framework.core.util.RequestDetailThreadLocal;
-import io.geekidea.springbootplus.system.exception.SysLoginException;
-import io.geekidea.springbootplus.system.exception.VerificationCodeException;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.authz.UnauthenticatedException;
-import org.apache.shiro.authz.UnauthorizedException;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.converter.HttpMessageNotReadableException;
-import org.springframework.validation.BindingResult;
-import org.springframework.validation.FieldError;
-import org.springframework.web.HttpMediaTypeException;
-import org.springframework.web.HttpRequestMethodNotSupportedException;
-import org.springframework.web.bind.MethodArgumentNotValidException;
-import org.springframework.web.bind.annotation.ControllerAdvice;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import org.springframework.web.bind.annotation.ResponseStatus;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * @author geekidea
- * @date 2018-11-08
- */
-@ControllerAdvice
-@RestController
-@Slf4j
-public class GlobalExceptionHandler {
-
-    /**
-     * 非法参数验证异常
-     *
-     * @param ex
-     * @return
-     */
-    @ExceptionHandler(MethodArgumentNotValidException.class)
-    @ResponseStatus(value = HttpStatus.OK)
-    public ApiResult<List<String>> handleMethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex) {
-        printRequestDetail();
-        BindingResult bindingResult = ex.getBindingResult();
-        List<String> list = new ArrayList<>();
-        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
-        for (FieldError fieldError : fieldErrors) {
-            list.add(fieldError.getDefaultMessage());
-        }
-        Collections.sort(list);
-        log.error(getApiCodeString(ApiCode.PARAMETER_EXCEPTION) + ":" + JSON.toJSONString(list));
-        return ApiResult.fail(ApiCode.PARAMETER_EXCEPTION, list);
-    }
-
-    /**
-     * 系统登录异常处理
-     *
-     * @param exception
-     * @return
-     */
-    @ExceptionHandler(value = SysLoginException.class)
-    @ResponseStatus(HttpStatus.OK)
-    public ApiResult<Boolean> sysLoginExceptionHandler(SysLoginException exception) {
-        printRequestDetail();
-        printApiCodeException(ApiCode.LOGIN_EXCEPTION, exception);
-        return ApiResult.fail(ApiCode.LOGIN_EXCEPTION);
-    }
-
-
-    /**
-     * HTTP解析请求参数异常
-     *
-     * @param exception
-     * @return
-     */
-    @ExceptionHandler(value = HttpMessageNotReadableException.class)
-    @ResponseStatus(HttpStatus.OK)
-    public ApiResult<Boolean> httpMessageNotReadableException(HttpMessageNotReadableException exception) {
-        printRequestDetail();
-        printApiCodeException(ApiCode.PARAMETER_EXCEPTION, exception);
-        return ApiResult.fail(ApiCode.PARAMETER_EXCEPTION);
-    }
-
-    /**
-     * HTTP
-     *
-     * @param exception
-     * @return
-     */
-    @ExceptionHandler(value = HttpMediaTypeException.class)
-    @ResponseStatus(HttpStatus.OK)
-    public ApiResult<Boolean> httpMediaTypeException(HttpMediaTypeException exception) {
-        printRequestDetail();
-        printApiCodeException(ApiCode.HTTP_MEDIA_TYPE_EXCEPTION, exception);
-        return ApiResult.fail(ApiCode.HTTP_MEDIA_TYPE_EXCEPTION);
-    }
-
-    /**
-     * 自定义业务/数据异常处理
-     *
-     * @param exception
-     * @return
-     */
-    @ExceptionHandler(value = {SpringBootPlusException.class})
-    @ResponseStatus(HttpStatus.OK)
-    public ApiResult<Boolean> springBootPlusExceptionHandler(SpringBootPlusException exception) {
-        printRequestDetail();
-        log.error("springBootPlusException:", exception);
-        int errorCode;
-        if (exception instanceof BusinessException) {
-            errorCode = ApiCode.BUSINESS_EXCEPTION.getCode();
-        } else if (exception instanceof DaoException) {
-            errorCode = ApiCode.DAO_EXCEPTION.getCode();
-        } else if (exception instanceof VerificationCodeException) {
-            errorCode = ApiCode.VERIFICATION_CODE_EXCEPTION.getCode();
-        } else {
-            errorCode = ApiCode.SPRING_BOOT_PLUS_EXCEPTION.getCode();
-        }
-        return new ApiResult<Boolean>()
-                .setCode(errorCode)
-                .setMessage(exception.getMessage());
-    }
-
-
-    /**
-     * 登录授权异常处理
-     *
-     * @param exception
-     * @return
-     */
-    @ExceptionHandler(value = AuthenticationException.class)
-    @ResponseStatus(HttpStatus.OK)
-    public ApiResult<Boolean> authenticationExceptionHandler(AuthenticationException exception) {
-        printRequestDetail();
-        printApiCodeException(ApiCode.AUTHENTICATION_EXCEPTION, exception);
-        return ApiResult.fail(ApiCode.AUTHENTICATION_EXCEPTION);
-    }
-
-
-    /**
-     * 未认证异常处理
-     *
-     * @param exception
-     * @return
-     */
-    @ExceptionHandler(value = UnauthenticatedException.class)
-    @ResponseStatus(HttpStatus.OK)
-    public ApiResult<Boolean> unauthenticatedExceptionHandler(UnauthenticatedException exception) {
-        printRequestDetail();
-        printApiCodeException(ApiCode.UNAUTHENTICATED_EXCEPTION, exception);
-        return ApiResult.fail(ApiCode.UNAUTHENTICATED_EXCEPTION);
-    }
-
-
-    /**
-     * 未授权异常处理
-     *
-     * @param exception
-     * @return
-     */
-    @ExceptionHandler(value = UnauthorizedException.class)
-    @ResponseStatus(HttpStatus.OK)
-    public ApiResult<Boolean> unauthorizedExceptionHandler(UnauthorizedException exception) {
-        printRequestDetail();
-        printApiCodeException(ApiCode.UNAUTHORIZED_EXCEPTION, exception);
-        return ApiResult.fail(ApiCode.UNAUTHORIZED_EXCEPTION);
-    }
-
-
-    /**
-     * JWT Token解析异常
-     *
-     * @param exception
-     * @return
-     */
-    @ExceptionHandler(value = JWTDecodeException.class)
-    @ResponseStatus(HttpStatus.OK)
-    public ApiResult<Boolean> jWTDecodeExceptionHandler(JWTDecodeException exception) {
-        printRequestDetail();
-        printApiCodeException(ApiCode.JWTDECODE_EXCEPTION, exception);
-        return ApiResult.fail(ApiCode.JWTDECODE_EXCEPTION);
-    }
-
-    /**
-     * 默认的异常处理
-     *
-     * @param exception
-     * @return
-     */
-    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
-    @ResponseStatus(HttpStatus.OK)
-    public ApiResult<String> httpRequestMethodNotSupportedExceptionHandler(Exception exception) {
-        printRequestDetail();
-        printApiCodeException(ApiCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION, exception);
-        return ApiResult.fail(ApiCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION.getCode(), exception.getMessage());
-    }
-
-    /**
-     * 默认的异常处理
-     *
-     * @param exception
-     * @return
-     */
-    @ExceptionHandler(value = Exception.class)
-    @ResponseStatus(HttpStatus.OK)
-    public ApiResult<Boolean> exceptionHandler(Exception exception) {
-        printRequestDetail();
-        printApiCodeException(ApiCode.SYSTEM_EXCEPTION, exception);
-        return ApiResult.fail(ApiCode.SYSTEM_EXCEPTION);
-    }
-
-
-    /**
-     * 打印请求详情
-     */
-    private void printRequestDetail() {
-        RequestDetail requestDetail = RequestDetailThreadLocal.getRequestDetail();
-        if (requestDetail != null) {
-            log.error("异常来源：ip: {}, path: {}", requestDetail.getIp(), requestDetail.getPath());
-        }
-    }
-
-    /**
-     * 获取ApiCode格式化字符串
-     *
-     * @param apiCode
-     * @return
-     */
-    private String getApiCodeString(ApiCode apiCode) {
-        if (apiCode != null) {
-            return String.format("errorCode: %s, errorMessage: %s", apiCode.getCode(), apiCode.getMessage());
-        }
-        return null;
-    }
-
-    /**
-     * 打印错误码及异常
-     *
-     * @param apiCode
-     * @param exception
-     */
-    private void printApiCodeException(ApiCode apiCode, Exception exception) {
-        log.error(getApiCodeString(apiCode), exception);
-    }
-
-}
diff --git a/bootstrap/src/test/java/io/geekidea/springbootplus/test/BaseTest.java b/bootstrap/src/test/java/io/geekidea/springbootplus/test/BaseTest.java
deleted file mode 100644
index 22dcddf2..00000000
--- a/bootstrap/src/test/java/io/geekidea/springbootplus/test/BaseTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.test;
-
-import io.geekidea.springbootplus.SpringBootPlusApplication;
-import org.junit.runner.RunWith;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringRunner;
-
-/**
- * @author geekidea
- * @date 2020/3/16
- **/
-@RunWith(SpringRunner.class)
-@SpringBootTest(classes = SpringBootPlusApplication.class)
-public class BaseTest {
-}
diff --git a/bootstrap/src/test/java/io/geekidea/springbootplus/test/FooBarMapperTest.java b/bootstrap/src/test/java/io/geekidea/springbootplus/test/FooBarMapperTest.java
deleted file mode 100644
index f53872c8..00000000
--- a/bootstrap/src/test/java/io/geekidea/springbootplus/test/FooBarMapperTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.test;
-
-import com.example.foobar.entity.FooBar;
-import com.example.foobar.mapper.FooBarMapper;
-import org.junit.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-
-/**
- * @author geekidea
- * @date 2020/3/16
- **/
-public class FooBarMapperTest extends BaseTest{
-
-    @Autowired
-    private FooBarMapper fooBarMapper;
-
-    @Test
-    public void test(){
-        FooBar addFooBar = new FooBar()
-                .setFoo("junit foo")
-                .setBar("junit bar")
-                .setName("test")
-                .setRemark("备注");
-        int addResult = fooBarMapper.insert(addFooBar);
-        System.out.println("addResult = " + addResult);
-
-        Long addId = addFooBar.getId();
-        System.out.println("addId = " + addId);
-
-        FooBar fooBar = fooBarMapper.selectById(addId);
-        System.out.println("fooBar = " + fooBar);
-
-    }
-
-}
diff --git a/build-docker.sh b/build-docker.sh
new file mode 100644
index 00000000..5f068f5a
--- /dev/null
+++ b/build-docker.sh
@@ -0,0 +1,10 @@
+# 1. maven打包
+mvn clean package -Pdemo
+
+# 2. 在Dockerfile文件目录下，构建docker镜像，spring-boot-plus为镜像名称
+docker build -t spring-boot-plus .
+
+# 3. 将镜像导出为tar
+docker save spring-boot-plus -o spring-boot-plus.tar
+
+
diff --git a/config/.gitignore b/config/.gitignore
deleted file mode 100644
index ccdf702f..00000000
--- a/config/.gitignore
+++ /dev/null
@@ -1,36 +0,0 @@
-/target/
-/classes
-!.mvn/wrapper/maven-wrapper.jar
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-
-### NetBeans ###
-/nbproject/private/
-/build/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-
-.DS_Store
-
-*.log
-logs
-
-*.rdb
-
-
-
diff --git a/config/README.md b/config/README.md
deleted file mode 100644
index 792b62fb..00000000
--- a/config/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# config 项目配置模块
\ No newline at end of file
diff --git a/config/pom.xml b/config/pom.xml
deleted file mode 100644
index 0280db66..00000000
--- a/config/pom.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>io.geekidea.springbootplus</groupId>
-        <artifactId>parent</artifactId>
-        <version>2.0</version>
-    </parent>
-
-    <artifactId>config</artifactId>
-    <name>config</name>
-    <description>项目配置模块</description>
-
-    <build>
-        <resources>
-            <resource>
-                <directory>src/main/resources</directory>
-                <filtering>true</filtering>
-                <excludes>
-                    <exclude>config/application-*.yml</exclude>
-                </excludes>
-            </resource>
-            <resource>
-                <directory>src/main/resources/config</directory>
-                <filtering>true</filtering>
-                <targetPath>config</targetPath>
-                <includes>
-                    <include>application-${profileActive}.yml</include>
-                </includes>
-            </resource>
-        </resources>
-    </build>
-
-</project>
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/constant/CacheKey.java b/config/src/main/java/io/geekidea/springbootplus/config/constant/CacheKey.java
deleted file mode 100644
index 8d348c8f..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/constant/CacheKey.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.constant;
-
-/**
- * 可排序查询参数对象
- *
- * @author geekidea
- * @since 2019-08-08
- */
-public interface CacheKey {
-
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/constant/CommonConstant.java b/config/src/main/java/io/geekidea/springbootplus/config/constant/CommonConstant.java
deleted file mode 100644
index 634d3d27..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/constant/CommonConstant.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.constant;
-
-/**
- * 公共常量
- *
- * @author geekidea
- * @date 2018-11-08
- */
-public interface CommonConstant {
-
-    /**
-     * 默认页码为1
-     */
-    Long DEFAULT_PAGE_INDEX = 1L;
-
-    /**
-     * 默认页大小为10
-     */
-    Long DEFAULT_PAGE_SIZE = 10L;
-
-    /**
-     * 分页总行数名称
-     */
-    String PAGE_TOTAL_NAME = "total";
-
-    /**
-     * 分页数据列表名称
-     */
-    String PAGE_RECORDS_NAME = "records";
-
-    /**
-     * 分页当前页码名称
-     */
-    String PAGE_INDEX_NAME = "pageIndex";
-
-    /**
-     * 分页当前页大小名称
-     */
-    String PAGE_SIZE_NAME = "pageSize";
-
-    /**
-     * 登录用户
-     */
-    String LOGIN_SYS_USER = "loginSysUser";
-
-    /**
-     * 登录token
-     */
-    String JWT_DEFAULT_TOKEN_NAME = "token";
-
-    /**
-     * JWT用户名
-     */
-    String JWT_USERNAME = "username";
-
-    /**
-     * JWT刷新新token响应状态码
-     */
-    int JWT_REFRESH_TOKEN_CODE = 460;
-
-    /**
-     * JWT刷新新token响应状态码，
-     * Redis中不存在，但jwt未过期，不生成新的token，返回361状态码
-     */
-    int JWT_INVALID_TOKEN_CODE = 461;
-
-    /**
-     * JWT Token默认密钥
-     */
-    String JWT_DEFAULT_SECRET = "666666";
-
-    /**
-     * JWT 默认过期时间，3600L，单位秒
-     */
-    Long JWT_DEFAULT_EXPIRE_SECOND = 3600L;
-
-    /**
-     * 默认头像
-     */
-    String DEFAULT_HEAD_URL = "";
-
-    /**
-     * 管理员角色名称
-     */
-    String ADMIN_ROLE_NAME = "管理员";
-
-    String ADMIN_LOGIN = "adminLogin";
-
-    /**
-     * 验证码token
-     */
-    String VERIFY_TOKEN = "verifyToken";
-
-    /**
-     * 图片
-     */
-    String IMAGE = "image";
-
-    /**
-     * JPEG
-     */
-    String JPEG = "JPEG";
-
-    /**
-     * base64前缀
-     */
-    String BASE64_PREFIX = "data:image/png;base64,";
-
-    /**
-     * ..
-     */
-    String SPOT_SPOT = "..";
-
-    /**
-     * ../
-     */
-    String SPOT_SPOT_BACKSLASH = "../";
-
-    /**
-     * SpringBootAdmin登录信息
-     */
-    String ADMIN_LOGIN_SESSION = "adminLoginSession";
-
-    /**
-     * 用户浏览器代理
-     */
-    String USER_AGENT = "User-Agent";
-
-    /**
-     * 本机地址IP
-     */
-    String LOCALHOST_IP = "127.0.0.1";
-    /**
-     * 本机地址名称
-     */
-    String LOCALHOST_IP_NAME = "本机地址";
-    /**
-     * 局域网IP
-     */
-    String LAN_IP = "192.168";
-    /**
-     * 局域网名称
-     */
-    String LAN_IP_NAME = "局域网";
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/constant/CommonRedisKey.java b/config/src/main/java/io/geekidea/springbootplus/config/constant/CommonRedisKey.java
deleted file mode 100644
index f3d79bd1..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/constant/CommonRedisKey.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.constant;
-
-/**
- * <p>
- *  redis key 常量
- * </p>
- * @author geekidea
- * @date 2019-05-23
- **/
-public interface CommonRedisKey {
-
-    /**
-     * 登录用户token信息key
-     * login:token:tokenMd5
-     */
-    String LOGIN_TOKEN = "login:token:%s";
-
-    /**
-     * 登录用户信息key
-     * login:user:username
-     */
-    String LOGIN_USER = "login:user:%s";
-
-    /**
-     * 登录用户盐值信息key
-     * login:salt:username
-     */
-    String LOGIN_SALT= "login:salt:%s";
-
-    /**
-     * 登录用户username token
-     * login:user:token:username:token
-     */
-    String LOGIN_USER_TOKEN = "login:user:token:%s:%s";
-
-    /**
-     * 登录用户下的所有token
-     * login:user:token:username:*
-     */
-    String LOGIN_USER_ALL_TOKEN = "login:user:token:%s:*";
-
-    /**
-     * 验证码
-     * verify.code:666666
-     */
-    String VERIFY_CODE = "verify.code:%s";
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/constant/DatePattern.java b/config/src/main/java/io/geekidea/springbootplus/config/constant/DatePattern.java
deleted file mode 100644
index a7d5735e..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/constant/DatePattern.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.constant;
-
-/**
- * <p>
- *     日期格式常量
- * </p>
- * @author geekidea
- * @date 2018-11-08
- */
-public interface DatePattern {
-
-    /**
-     * 年-月-日
-     */
-    String YYYY_MM_DD = "yyyy-MM-dd";
-    /**
-     * 年-月-日 时:分
-     */
-    String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
-    /**
-     * 年-月-日 时:分:秒
-     */
-    String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
-    /**
-     * 年-月-日 时:分:秒:毫秒
-     */
-    String YYYY_MM_DD_HH_MM_SS_S = "yyyy-MM-dd HH:mm:ss.S";
-
-    /**
-     * 时:分
-     */
-    String HH_MM = "HH:mm";
-    /**
-     * 时:分:秒
-     */
-    String HH_MM_SS = "HH:mm:ss";
-    /**
-     * 时:分:秒:毫秒
-     */
-    String HH_MM_SS_S = "HH:mm:ss:S";
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/properties/JwtProperties.java b/config/src/main/java/io/geekidea/springbootplus/config/properties/JwtProperties.java
deleted file mode 100644
index c66377ef..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/properties/JwtProperties.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.properties;
-
-import io.geekidea.springbootplus.config.constant.CommonConstant;
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
-
-/**
- * JWT属性配置
- *
- * @author geekidea
- * @date 2019-05-22
- **/
-@Data
-@Component
-@ConfigurationProperties(prefix = "spring-boot-plus.jwt")
-public class JwtProperties {
-
-    /**
-     * token名称,默认名称为：token，可自定义
-     */
-    private String tokenName = CommonConstant.JWT_DEFAULT_TOKEN_NAME;
-
-    /**
-     * 密码
-     */
-    private String secret = CommonConstant.JWT_DEFAULT_SECRET;
-
-    /**
-     * 签发人
-     */
-    private String issuer;
-
-    /**
-     * 主题
-     */
-    private String subject;
-
-    /**
-     * 签发的目标
-     */
-    private String audience;
-
-    /**
-     * token失效时间,默认1小时，60*60=3600
-     */
-    private Long expireSecond = CommonConstant.JWT_DEFAULT_EXPIRE_SECOND;
-
-    /**
-     * 是否刷新token，默认为true
-     */
-    private boolean refreshToken = true;
-
-    /**
-     * 刷新token倒计时，默认10分钟，10*60=600
-     */
-    private Integer refreshTokenCountdown;
-
-    /**
-     * redis校验jwt token是否存在
-     */
-    private boolean redisCheck;
-
-    /**
-     * 单用户登录，一个用户只能又一个有效的token
-     */
-    private boolean singleLogin;
-
-    /**
-     * 是否进行盐值校验
-     */
-    private boolean saltCheck;
-
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/properties/ShiroPermissionProperties.java b/config/src/main/java/io/geekidea/springbootplus/config/properties/ShiroPermissionProperties.java
deleted file mode 100644
index 39847e93..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/properties/ShiroPermissionProperties.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.properties;
-
-import lombok.Data;
-
-/**
- * Shiro权限配置映射类
- *
- * @author geekidea
- * @date 2019-09-28
- **/
-@Data
-public class ShiroPermissionProperties {
-
-    /**
-     * 路径
-     */
-    private String url;
-    /**
-     * 路径数组
-     */
-    private String[] urls;
-
-    /**
-     * 权限
-     */
-    private String permission;
-
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/properties/ShiroProperties.java b/config/src/main/java/io/geekidea/springbootplus/config/properties/ShiroProperties.java
deleted file mode 100644
index 3acc6ba3..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/properties/ShiroProperties.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.properties;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-import java.util.List;
-
-/**
- * Shiro配置映射类
- *
- * @author geekidea
- * @date 2019-09-28
- **/
-@Data
-@ConfigurationProperties(prefix = "spring-boot-plus.shiro")
-public class ShiroProperties {
-
-    /**
-     * 是否启用
-     */
-    private boolean enable;
-
-    /**
-     * 路径权限配置
-     */
-    private String filterChainDefinitions;
-
-    /**
-     * 设置无需权限路径集合
-     */
-    private List<String[]> anon;
-
-    /**
-     * 权限配置集合
-     */
-    @NestedConfigurationProperty
-    private List<ShiroPermissionProperties> permission;
-
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusAopProperties.java b/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusAopProperties.java
deleted file mode 100644
index d294b60f..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusAopProperties.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.properties;
-
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-import org.springframework.stereotype.Component;
-
-import java.util.Set;
-
-/**
- * AOP配置属性
- *
- * @author geekidea
- * @date 2019-09-29
- **/
-@Data
-@Component
-@ConfigurationProperties(prefix = "spring-boot-plus.aop")
-public class SpringBootPlusAopProperties {
-
-    /**
-     * 请求路径Filter配置
-     */
-    @NestedConfigurationProperty
-    private LogAopConfig log = new LogAopConfig();
-
-    /**
-     * 操作日志配置
-     */
-    @NestedConfigurationProperty
-    private OperationLogConfig operationLog;
-
-    /**
-     * 登录日志配置
-     */
-    private LoginLogConfig loginLog;
-
-    @Data
-    public static class AopConfig {
-
-        /**
-         * 是否启用
-         */
-        private boolean enable;
-
-    }
-
-    @Data
-    @EqualsAndHashCode(callSuper = true)
-    public static class LogAopConfig extends AopConfig {
-
-        /**
-         * 是否启用requestId
-         */
-        private boolean enableRequestId = true;
-
-        /**
-         * 日志输出类型：print-type
-         */
-        private LogPrintType logPrintType = LogPrintType.ORDER;
-
-        /**
-         * 请求ID生成类型
-         */
-        private RequestIdType requestIdType = RequestIdType.IDWORK;
-
-        /**
-         * 请求日志在控制台是否格式化输出，local环境建议开启，服务器环境设置为false
-         */
-        private boolean requestLogFormat = true;
-
-        /**
-         * 响应日志在控制台是否格式化输出，local环境建议开启，服务器环境设置为false
-         */
-        private boolean responseLogFormat = true;
-
-        /**
-         * 排除路径
-         */
-        private Set<String> excludePaths;
-    }
-
-    /**
-     * 操作日志配置
-     */
-    @Data
-    public static class OperationLogConfig {
-
-        /**
-         * 是否启用
-         */
-        private boolean enable = true;
-
-        /**
-         * 排除路径
-         */
-        private Set<String> excludePaths;
-
-    }
-
-    /**
-     * 登录日志配置
-     */
-    @Data
-    public static class LoginLogConfig {
-
-        /**
-         * 是否启用
-         */
-        private boolean enable = true;
-
-        /**
-         * 登录路径
-         */
-        private String loginPath = "/login";
-
-        /**
-         * 登出路径
-         */
-        private String logoutPath = "/logout";
-
-    }
-
-    /**
-     * 日志打印类型
-     *
-     * @author geekidea
-     * @date 2020/3/19
-     **/
-    public enum LogPrintType {
-
-        /**
-         * 不打印日志
-         */
-        NONE,
-        /**
-         * 请求和响应日志，按照执行顺序分开打印
-         */
-        ORDER,
-        /**
-         * 方法执行结束时，连续分开打印请求和响应日志
-         */
-        LINE,
-        /**
-         * 方法执行结束时，合并请求和响应日志，同时打印
-         */
-        MERGE;
-
-    }
-
-    /**
-     * 请求ID生成类型
-     *
-     * @author geekidea
-     * @date 2020/3/25
-     **/
-    public enum RequestIdType {
-        /**
-         * 生成UUID无中横线
-         */
-        UUID,
-        /**
-         * 生成数字
-         */
-        IDWORK
-    }
-
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusCorsProperties.java b/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusCorsProperties.java
deleted file mode 100644
index b6eda2b4..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusCorsProperties.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.properties;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
-import org.springframework.web.cors.CorsConfiguration;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * 跨域属性配置
- *
- * @author geekidea
- * @date 2019-10-09
- **/
-@Data
-@Component
-@ConfigurationProperties(prefix = "spring-boot-plus.cors")
-public class SpringBootPlusCorsProperties {
-
-    /**
-     * 是否启用跨域，默认启用
-     */
-    private boolean enable = true;
-
-    /**
-     * CORS过滤的路径，默认：/**
-     */
-    private String path = "/**";
-
-    /**
-     * 允许访问的源
-     */
-    private List<String> allowedOrigins = Collections.singletonList(CorsConfiguration.ALL);
-
-    /**
-     * 允许访问的请求头
-     */
-    private List<String> allowedHeaders = Collections.singletonList(CorsConfiguration.ALL);
-
-    /**
-     * 是否允许发送cookie
-     */
-    private boolean allowCredentials = true;
-
-    /**
-     * 允许访问的请求方式
-     */
-    private List<String> allowedMethods = Collections.singletonList(CorsConfiguration.ALL);
-
-    /**
-     * 允许响应的头
-     */
-    private List<String> exposedHeaders = Arrays.asList("token");
-
-    /**
-     * 该响应的有效时间默认为30分钟，在有效时间内，浏览器无须为同一请求再次发起预检请求
-     */
-    private Long maxAge = 1800L;
-
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusFilterProperties.java b/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusFilterProperties.java
deleted file mode 100644
index 16fc40d8..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusFilterProperties.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.properties;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-import org.springframework.stereotype.Component;
-
-/**
- * Filter配置属性
- *
- * @author geekidea
- * @date 2019-09-29
- **/
-@Data
-@Component
-@ConfigurationProperties(prefix = "spring-boot-plus.filter")
-public class SpringBootPlusFilterProperties {
-
-    /**
-     * RequestDetail Filter配置
-     */
-    @NestedConfigurationProperty
-    private FilterConfig request = new FilterConfig();
-
-    /**
-     * XSS Filter配置
-     */
-    @NestedConfigurationProperty
-    private FilterConfig xss = new FilterConfig();
-
-    @Data
-    public static class FilterConfig {
-
-        /**
-         * 是否启用
-         */
-        private boolean enable;
-
-        /**
-         * 过滤的路径
-         */
-        private String[] urlPatterns;
-
-        /**
-         * 排序
-         */
-        private int order;
-
-        /**
-         * 是否支持异步
-         */
-        private boolean async;
-
-    }
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusInterceptorProperties.java b/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusInterceptorProperties.java
deleted file mode 100644
index 5224a783..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusInterceptorProperties.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.properties;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-import org.springframework.stereotype.Component;
-
-/**
- * spring-boot-plus拦截器配置属性
- *
- * @author geekidea
- * @date 2019/8/5
- */
-@Data
-@Component
-@ConfigurationProperties(prefix = "spring-boot-plus.interceptor")
-public class SpringBootPlusInterceptorProperties {
-
-    /**
-     * SpringBootAdmin权限拦截器
-     */
-    private InterceptorConfig springbootadmin = new InterceptorConfig();
-
-    /**
-     * 自定义权限拦截器
-     */
-    @NestedConfigurationProperty
-    private InterceptorConfig permission = new InterceptorConfig();
-
-    /**
-     * 资源拦截器
-     */
-    @NestedConfigurationProperty
-    private InterceptorConfig resource = new InterceptorConfig();
-
-    /**
-     * 上传拦截器
-     */
-    @NestedConfigurationProperty
-    private InterceptorConfig upload = new InterceptorConfig();
-
-    /**
-     * 下载拦截器
-     */
-    @NestedConfigurationProperty
-    private InterceptorConfig download = new InterceptorConfig();
-
-    @Data
-    public static class InterceptorConfig {
-
-        /**
-         * 是否启用
-         */
-        private boolean enable;
-
-        /**
-         * 包含的路径
-         */
-        private String[] includePaths = new String[]{};
-
-        /**
-         * 排除路径
-         */
-        private String[] excludePaths = new String[]{};
-
-
-    }
-
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusProperties.java b/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusProperties.java
deleted file mode 100644
index b486ad9e..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusProperties.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.properties;
-
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-import org.springframework.stereotype.Component;
-
-import java.util.List;
-
-/**
- * spring-boot-plus属性配置信息
- *
- * @author geekidea
- * @date 2019-08-04
- */
-@Data
-@Component
-@ConfigurationProperties(prefix = "spring-boot-plus")
-public class SpringBootPlusProperties {
-
-    /**
-     * 是否启用ansi控制台输出有颜色的字体，local环境建议开启，服务器环境设置为false
-     */
-    private boolean enableAnsi;
-
-    /**
-     * 项目IP或域名地址
-     */
-    private String serverIp;
-
-    /**
-     * 是否启用验证码
-     */
-    private boolean enableVerifyCode;
-
-    /**
-     * 新建登录用户初始化盐值
-     */
-    private String loginInitSalt;
-
-    /**
-     * 新建登录用户初始化密码
-     */
-    private String loginInitPassword;
-
-    /**
-     * 新建用户初始化头像
-     */
-    private String loginInitHead;
-
-    /**
-     * 实现BaseEnum接口的枚举包
-     */
-    private String[] enumPackages;
-
-    /**
-     * 拦截器配置
-     */
-    @NestedConfigurationProperty
-    private SpringBootPlusInterceptorProperties interceptor;
-
-    /**
-     * 过滤器配置
-     */
-    @NestedConfigurationProperty
-    private SpringBootPlusFilterProperties filter;
-
-    /**
-     * 上传目录
-     */
-    private String uploadPath;
-    /**
-     * 资源访问路径，前端访问
-     */
-    private String resourceAccessPath;
-    /**
-     * 资源访问路径，后段配置，资源映射/拦截器使用
-     */
-    private String resourceAccessPatterns;
-    /**
-     * 资源访问全路径
-     */
-    private String resourceAccessUrl;
-
-    /**
-     * 允许上传的文件后缀集合
-     */
-    private List<String> allowUploadFileExtensions;
-    /**
-     * 允许下载的文件后缀集合
-     */
-    private List<String> allowDownloadFileExtensions;
-
-    /**
-     * JWT配置
-     */
-    @NestedConfigurationProperty
-    private JwtProperties jwt;
-
-    /**
-     * Shiro配置
-     */
-    @NestedConfigurationProperty
-    private ShiroProperties shiro = new ShiroProperties();
-
-    /**
-     * 项目静态资源访问配置
-     *
-     * @see SpringBootPlusWebMvcConfig addResourceHandlers
-     */
-    private String resourceHandlers;
-
-    /**
-     * 跨域配置
-     */
-    @NestedConfigurationProperty
-    private SpringBootPlusCorsProperties cors = new SpringBootPlusCorsProperties();
-
-    /**
-     * Swagger路径
-     */
-    private List<String> swaggerPaths;
-
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusStaticProperties.java b/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusStaticProperties.java
deleted file mode 100644
index 5e338c8b..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/properties/SpringBootPlusStaticProperties.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.properties;
-
-import lombok.Data;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.PostConstruct;
-
-/**
- * 配置文件属性映射为静态属性
- *
- * @author geekidea
- * @date 2019-10-11
- **/
-@Slf4j
-@Data
-@Component
-public class SpringBootPlusStaticProperties {
-
-    public static String INFO_PROJECT_VERSION = "";
-
-    @Value("${info.project-version}")
-    private String infoProjectVersion;
-
-    @PostConstruct
-    public void init() {
-        INFO_PROJECT_VERSION = this.infoProjectVersion;
-        log.debug("INFO_PROJECT_VERSION:" + INFO_PROJECT_VERSION);
-    }
-
-}
diff --git a/config/src/main/java/io/geekidea/springbootplus/config/properties/SwaggerProperties.java b/config/src/main/java/io/geekidea/springbootplus/config/properties/SwaggerProperties.java
deleted file mode 100644
index c400475b..00000000
--- a/config/src/main/java/io/geekidea/springbootplus/config/properties/SwaggerProperties.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config.properties;
-
-import lombok.Data;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-import org.springframework.stereotype.Component;
-
-import java.util.List;
-
-/**
- * Swagger配置属性
- *
- * @author geekidea
- * @date 2020/3/21
- **/
-@Data
-@Component
-@ConfigurationProperties(prefix = "spring-boot-plus.swagger")
-public class SwaggerProperties {
-
-    /**
-     * 是否启用Swagger
-     */
-    private boolean enable;
-
-    /**
-     * 扫描的基本包
-     */
-    @Value("${spring-boot-plus.swagger.base.package}")
-    private String basePackage;
-
-    /**
-     * 联系人邮箱
-     */
-    @Value("${spring-boot-plus.swagger.contact.email}")
-    private String contactEmail;
-
-    /**
-     * 联系人名称
-     */
-    @Value("${spring-boot-plus.swagger.contact.name}")
-    private String contactName;
-
-    /**
-     * 联系人网址
-     */
-    @Value("${spring-boot-plus.swagger.contact.url}")
-    private String contactUrl;
-
-    /**
-     * 描述
-     */
-    private String description;
-
-    /**
-     * 标题
-     */
-    private String title;
-
-    /**
-     * 网址
-     */
-    private String url;
-
-    /**
-     * 版本
-     */
-    private String version;
-
-    /**
-     * 自定义参数配置
-     */
-    @NestedConfigurationProperty
-    private List<ParameterConfig> parameterConfig;
-
-    /**
-     * 自定义参数配置
-     */
-    @Data
-    public static class ParameterConfig {
-
-        /**
-         * 名称
-         */
-        private String name;
-
-        /**
-         * 描述
-         */
-        private String description;
-
-        /**
-         * 参数类型
-         * header, cookie, body, query
-         */
-        private String type = "head";
-
-        /**
-         * 数据类型
-         */
-        private String dataType = "String";
-
-        /**
-         * 是否必填
-         */
-        private boolean required;
-
-        /**
-         * 默认值
-         */
-        private String defaultValue;
-
-    }
-}
diff --git a/config/src/main/resources/config/application-dev.yml b/config/src/main/resources/config/application-dev.yml
deleted file mode 100644
index eb3e3dcf..00000000
--- a/config/src/main/resources/config/application-dev.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-spring-boot-plus:
-  # 是否启用ansi控制台输出有颜色的字体，dev环境建议开启，服务器环境设置为false
-  enable-ansi: true
-  # 当前环境服务IP地址
-  server-ip: 127.0.0.1
-  # 文件上传下载配置
-  upload-path: /opt/upload/
-
-
-spring:
-  datasource:
-    url: jdbc:mysql://localhost:3306/spring_boot_plus?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
-    username: root
-    password: root
-
-  # Redis配置
-  redis:
-    database: 0
-    host: localhost
-    password:
-    port: 6379
-
-# 打印SQL语句和结果集，本地开发环境可开启，线上注释掉
-mybatis-plus:
-  configuration:
-    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
\ No newline at end of file
diff --git a/config/src/main/resources/config/application-prod.yml b/config/src/main/resources/config/application-prod.yml
deleted file mode 100644
index 0e22cd9a..00000000
--- a/config/src/main/resources/config/application-prod.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-spring-boot-plus:
-  # 是否启用ansi控制台输出有颜色的字体，local环境建议开启，服务器环境设置为false
-  enable-ansi: false
-  # 当前环境服务IP地址
-  server-ip:
-  # 生产环境禁用Swagger
-  swagger:
-    enable: false
-  # AOP配置
-  aop:
-    log:
-      enable: true
-      log-print-type: NONE
-      request-log-format: false
-      response-log-format: false
-
-spring:
-  datasource:
-    url:
-    username:
-    password:
-
-  # Redis配置
-  redis:
-    database:
-    host:
-    password:
-    port:
-
diff --git a/config/src/main/resources/config/application-test.yml b/config/src/main/resources/config/application-test.yml
deleted file mode 100644
index a5eaa9c3..00000000
--- a/config/src/main/resources/config/application-test.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-spring-boot-plus:
-  # 是否启用ansi控制台输出有颜色的字体，local环境建议开启，服务器环境设置为false
-  enable-ansi: false
-  # 当前环境服务IP地址
-  server-ip: 47.105.159.10
-  # 文件上传下载配置
-  upload-path: /opt/upload/
-  # AOP配置
-  aop:
-    log:
-      enable: true
-      log-print-type: LINE
-      request-log-format: false
-      response-log-format: false
-
-spring:
-  datasource:
-    url: jdbc:mysql://localhost:3306/spring_boot_plus?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
-    username: root
-    password: Springbootplus666!
-
-  # Redis配置
-  redis:
-    database: 0
-    host: localhost
-    password:
-    port: 6379
-
-# 打印SQL语句和结果集，本地开发环境可开启，线上注释掉
-mybatis-plus:
-  configuration:
-    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-
diff --git a/config/src/main/resources/config/application-uat.yml b/config/src/main/resources/config/application-uat.yml
deleted file mode 100644
index 5eced9a3..00000000
--- a/config/src/main/resources/config/application-uat.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-spring-boot-plus:
-  # 是否启用ansi控制台输出有颜色的字体，local环境建议开启，服务器环境设置为false
-  enable-ansi: false
-  # 当前环境服务IP地址
-  server-ip:
-  # AOP配置
-  aop:
-    log:
-      enable: true
-      log-print-type: LINE
-      request-log-format: false
-      response-log-format: false
-
-spring:
-  datasource:
-    url:
-    username:
-    password:
-
-  # Redis配置
-  redis:
-    database:
-    host:
-    password:
-    port:
-
diff --git a/config/src/main/resources/config/application.yml b/config/src/main/resources/config/application.yml
deleted file mode 100644
index 831a00dd..00000000
--- a/config/src/main/resources/config/application.yml
+++ /dev/null
@@ -1,389 +0,0 @@
-# spring-boot-plus Common Config
-
-############################# 访问路径、端口tomcat start #############################
-server:
-  port: 8888
-  servlet:
-    context-path: /api
-  tomcat:
-    max-threads: 1000
-    min-spare-threads: 30
-    uri-encoding: UTF-8
-############################# 访问路径、端口tomcat end ###############################
-
-################################ spring config start ###############################
-spring:
-  application:
-    name: spring-boot-plus
-  http:
-    encoding:
-      charset: UTF-8
-      enabled: true
-      force: true
-  jackson:
-    date-format: yyy-MM-dd HH:mm:ss
-    time-zone: GMT+8
-  banner:
-    charset: UTF-8
-    location: classpath:config/banner.txt
-  servlet:
-    # 文件上传配置
-    multipart:
-      # 指定上传文件的临时目录
-      location: /opt/upload/tmp
-      # 单个文件最大值
-      max-file-size: 10MB
-      # 单个请求文件总计最大值
-      max-request-size: 100MB
-
-# 当前项目maven激活环境，例如：dev/test/uat/prod，对应pom.xml中profile设置值
----
-spring:
-  profiles:
-    active: @profileActive@
-
-# logback.xml中有详细的日志配置
-logging:
-  config: classpath:config/logback.xml
-  #  方便Spring Boot Admin页面上实时查看日志
-  file:
-    name: logs/${spring.application.name}.log
-################################ spring config end #################################
-
-
-############################## spring-boot-plus start ##############################
-spring-boot-plus:
-  # 是否启用ansi控制台输出有颜色的字体
-  enable-ansi: true
-  # 服务器IP地址
-  server-ip: 127.0.0.1
-  # 是否启用验证码
-  enable-verify-code: false
-  # 默认新建用户登录初始化密码
-  login-init-salt: 666666
-  login-init-password: admin
-  login-init-head: http://${spring-boot-plus.server-ip}:8888/api/resource/logo.png
-  # 实现BaseEnum接口的枚举包
-  enum-packages: com.example.foorbar.enums
-  # Swagger路径
-  swagger-paths: /swagger-ui.html,/docs,/doc.html,/swagger-resources/**,/webjars/**,/v2/api-docs,/csrf,/v2/api-docs-ext,/null/swagger-resources/**
-  # Filter配置
-  filter:
-    request:
-      enable: true
-      url-patterns: /*
-      order: 1
-      async: true
-    xss:
-      enable: true
-      url-patterns: /*
-      order: 2
-      async: true
-
-  # 拦截器配置
-  interceptor:
-    permission:
-      enable: false
-      include-paths: /**
-    resource:
-      enable: false
-      include-paths: ${spring-boot-plus.resource-access-patterns}
-    upload:
-      enable: false
-      include-paths: /upload/**
-    download:
-      enable: false
-      include-paths: /download/**
-
-  # AOP配置
-  aop:
-    # Aop日志配置
-    log:
-      # 是否启用
-      enable: true
-      # 是否启用requestId
-      enable-request-id: true
-      # requestId生成规则: UUID/IDWORK
-      request-id-type: IDWORK
-      # NONE：不打印日志
-      # ORDER：请求和响应日志，按照执行顺序分开打印
-      # LINE：方法执行结束时，连续分开打印请求和响应日志
-      # MERGE：方法执行结束时，合并请求和响应日志，同时打印
-      log-print-type: ORDER
-      # 请求日志在控制台是否格式化输出，local环境建议开启，服务器环境设置为false
-      request-log-format: true
-      # 响应日志在控制台是否格式化输出，local环境建议开启，服务器环境设置为false
-      response-log-format: true
-      # 排除的路径
-      exclude-paths: /,/csrf
-    # 操作日志配置
-    operation-log:
-      # 是否启用
-      enable: true
-      # 排除的路径
-      exclude-paths:
-    # 登录日志配置
-    login-log:
-      # 是否启用
-      enable: true
-      # 登录地址
-      login-path: /login
-      # 登出地址
-      logout-path: /logout
-
-  # 文件上传下载配置
-  # 上传路径配置
-  upload-path: /opt/upload/
-  # 资源访问路径
-  resource-access-path: /resource/
-  # 资源访问路径匹配：/resource/**
-  resource-access-patterns: ${spring-boot-plus.resource-access-path}**
-  # 资源访问全路径前缀：http://127.0.0.1:8888/resource/
-  resource-access-url: http://${spring-boot-plus.server-ip}:${server.port}${server.servlet.context-path}${spring-boot-plus.resource-access-path}
-  # 全局允许上传的类型
-  allow-upload-file-extensions: jpg,png,docx,xlsx,pptx,pdf
-  # 全局允许下载的类型
-  allow-download-file-extensions: jpg,png,docx,xlsx,pptx,pdf
-
-  ############################ CORS start ############################
-  # CORS跨域配置，默认允许跨域
-  cors:
-    # 是否启用跨域，默认启用
-    enable: true
-    # CORS过滤的路径，默认：/**
-    path: /**
-    # 允许访问的源
-    allowed-origins: '*'
-    # 允许访问的请求头
-    allowed-headers: '*'
-    # 是否允许发送cookie
-    allow-credentials: true
-    # 允许访问的请求方式
-    allowed-methods: OPTION,GET,POST
-    # 允许响应的头
-    exposed-headers: token
-    # 该响应的有效时间默认为30分钟，在有效时间内，浏览器无须为同一请求再次发起预检请求
-    max-age: 1800
-
-  ############################ CORS end ##############################
-
-  ########################## Resource start ##########################
-  # 静态资源访问配置
-  resource-handlers: |
-    /static/**=classpath:/static/
-    swagger-ui.html=classpath:/META-INF/resources/
-    /webjars/**=classpath:/META-INF/resources/webjars/
-    doc.html=classpath:/META-INF/resources/
-  ########################## Resource end ############################
-
-  ######################## Spring Shiro start ########################
-  shiro:
-    # 是否启用
-    enable: true
-    # 权限配置
-    anon:
-      # 排除登录登出
-      - /login,/logout
-      # 排除静态资源
-      - /static/**,/templates/**
-      # 排除Swagger
-      - ${spring-boot-plus.swagger-paths}
-      # 排除actuator
-      - /actuator/**
-      - # 排除首页
-      - /,/index.html
-      # 排除测试路径
-      - /hello/world,/fooBar/**,/exampleOrder/**
-    # 多行字符串权限配置
-    filter-chain-definitions: |
-      /resource/**=anon
-      /upload/**=anon
-      /verificationCode/**=anon
-      /enum=anon
-      /getSysUserInfo=anon
-
-  ######################## Spring Shiro end ##########################
-
-  ############################ JWT start #############################
-  jwt:
-    # token请求头名称
-    token-name: token
-    # jwt密钥
-    secret: 666666
-    # 发行人
-    issuer: ${spring.application.name}
-    # 观众
-    audience: web
-    # 默认过期时间1小时，单位：秒
-    expire-second: 36000
-    # 是否刷新token
-    refresh-token: true
-    # 刷新token的时间间隔，默认10分钟，单位：秒
-    refresh-token-countdown: 600
-    # redis校验jwt token是否存在,可选
-    redis-check: true
-    # true: 同一个账号只能是最后一次登录token有效，false：同一个账号可多次登录
-    single-login: false
-    # 盐值校验，如果不加自定义盐值，则使用secret校验
-    salt-check: true
-  ############################ JWT end ###############################
-
-############################### spring-boot-plus end ###############################
-
-
-############################### mybatis-plus start #################################
-mybatis-plus:
-  # 启动时是否检查MyBatis XML文件是否存在
-  check-config-location: true
-  # 支持统配符 * 或者 ; 分割
-  typeEnumsPackage: io.geekidea.springbootplus.*.enums
-  # MyBatis原生配置
-  configuration:
-    # 字段名称下划线转驼峰命名
-    map-underscore-to-camel-case: true
-  global-config:
-    db-config:
-      # 全局默认主键类型
-      id-type: auto
-      # 逻辑已删除值(默认为 1)
-      logic-delete-value: 1
-      # 逻辑未删除值(默认为 0)
-      logic-not-delete-value: 0
-  # mapper xml映射路径
-  mapper-locations: classpath*:mapper/**/*Mapper.xml
-################################ mybatis-plus end ##################################
-
-
-############################### HikariCP 数据源配置 start ################################
----
-spring:
-  datasource:
-    driver-class-name: com.mysql.jdbc.Driver
-    type: com.zaxxer.hikari.HikariDataSource
-    cachePrepStmts: true
-    prepStmtCacheSize: 300
-    prepStmtCacheSqlLimit: 2048
-    useServerPrepStmts: true
-    useLocalSessionState: true
-    rewriteBatchedStatements: true
-    cacheResultSetMetadata: true
-    cacheServerConfiguration: true
-    elideSetAutoCommits: true
-    maintainTimeStats: false
-    hikari:
-      minimum-idle: 10
-      maximum-pool-size: 20
-      idle-timeout: 10000
-      max-lifetime: 1800000
-      connection-timeout: 30000
-############################### HikariCP 数据源配置 end ################################
-
-
-############################### Redis 公共配置 start ###############################
----
-spring:
-  redis:
-    timeout: 10s
-    lettuce:
-      pool:
-        max-active: 200
-        max-idle: 8
-        max-wait: 10s
-        min-idle: 2
-      shutdown-timeout: 3s
-############################### Redis 公共配置 end ##################################
-
-
-#################################### Swagger start #################################
----
-spring-boot-plus:
-  swagger:
-    # 是否启用
-    enable: true
-    base:
-      # 扫描的包，多个包使用逗号隔开
-      package: io.geekidea.springbootplus,com.example
-    contact:
-      email: geekidea@qq.com
-      name: springboot.plus
-      url: https://springboot.plus
-    description:
-    title: ${spring.application.name} API Documents
-    url: https://springboot.plus
-    version: ${project.version}
-    # 自定义参数配置，可配置N个
-    parameter-config:
-      - name: ${spring-boot-plus.jwt.token-name}
-        description: Token Request Header
-        # header, cookie, body, query
-        type: header
-        data-type: String
-        required: false
-        # 测试接口时，自动填充token的值
-        default-value:
-
-# knife4j配置
-knife4j:
-  enable: ${spring-boot-plus.swagger.enable}
-  basic:
-    enable: true
-    username: admin
-    password: admin
-
-#################################### Swagger end ###################################
-
-
-############################## Spring boot admin start ##############################
----
-spring:
-  boot:
-    admin:
-      client:
-        # Spring Boot Admin服务地址，参照admin模块application.yml配置
-        url: http://${spring-boot-plus.server-ip}:8000
-        # Spring Boot Admin账号
-        username: admin
-        # Spring Boot Admin密码
-        password: admin
-        # 当前项目实例名称
-        instance:
-          # client名称
-          name: ${spring.application.name}
-          # client ip端口
-          service-base-url: http://${spring-boot-plus.server-ip}:${server.port}
-      monitor:
-        period: 100000
-        status-lifetime: 100000
-        connect-timeout: 100000
-        read-timeout: 100000
-
-# 开启和暴露端点
-management:
-  endpoint:
-    health:
-      show-details: ALWAYS
-    logfile:
-      #可在线查看日志
-      enabled: true
-  endpoints:
-    web:
-      exposure:
-        include: '*'
-
-# 自定义项目信息，Spring Boot Admin展示使用
-info:
-  project-groupId: '@project.parent.groupId@'
-  project-name: ${spring.application.name}
-  project-finalName: '@boot.artifact.name@'
-  project-author: geekidea
-  project-description: ${spring.application.name} project
-  project-sourceEncoding: '@project.build.sourceEncoding@'
-  project-spring-boot-version: '@spring-boot.version@'
-  project-mybatis-plus-version: '@mybatis-plus-boot-starter.version@'
-  project-version: '@project.version@'
-  project-website: 'https://springboot.plus'
-  project-home: http://${spring-boot-plus.server-ip}:${server.port}${server.servlet.context-path}
-  project-swagger: http://${spring-boot-plus.server-ip}:${server.port}${server.servlet.context-path}/swagger-ui.html
-  project-knife4j: http://${spring-boot-plus.server-ip}:${server.port}${server.servlet.context-path}/doc.html
-############################## Spring boot admin end ###############################
-
diff --git a/config/src/main/resources/config/mime-type.properties b/config/src/main/resources/config/mime-type.properties
deleted file mode 100644
index b992a862..00000000
--- a/config/src/main/resources/config/mime-type.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-#
-
-# 扩展mime-type
-# 参考：https://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?revision=1752884&view=co
-md=text/markdown
\ No newline at end of file
diff --git a/config/src/main/resources/static/404.html b/config/src/main/resources/static/404.html
deleted file mode 100644
index 6bbfb01b..00000000
--- a/config/src/main/resources/static/404.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <title>404</title>
-</head>
-<body>
-<h1>Not Found</h1>
-</body>
-</html>
\ No newline at end of file
diff --git a/config/src/main/resources/static/500.html b/config/src/main/resources/static/500.html
deleted file mode 100644
index 79309a5d..00000000
--- a/config/src/main/resources/static/500.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <title>500</title>
-</head>
-<body>
-<h1>500</h1>
-</body>
-</html>
\ No newline at end of file
diff --git a/config/src/main/resources/static/error.html b/config/src/main/resources/static/error.html
deleted file mode 100644
index c0e4e5a5..00000000
--- a/config/src/main/resources/static/error.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <title>404</title>
-</head>
-<body>
-<h1>Error</h1>
-</body>
-</html>
\ No newline at end of file
diff --git a/config/src/main/resources/static/favicon.ico b/config/src/main/resources/static/favicon.ico
deleted file mode 100644
index bd5a6781..00000000
Binary files a/config/src/main/resources/static/favicon.ico and /dev/null differ
diff --git a/config/src/main/resources/static/index.html b/config/src/main/resources/static/index.html
deleted file mode 100644
index 4579e3d7..00000000
--- a/config/src/main/resources/static/index.html
+++ /dev/null
@@ -1,543 +0,0 @@
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!doctype html>
-<html>
-<head>
-<meta charset='UTF-8'><meta name='viewport' content='width=device-width initial-scale=1'>
-<title>spring-boot-plus Home</title><link href='https://fonts.loli.net/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext' rel='stylesheet' type='text/css' /><style type='text/css'>html {overflow-x: initial !important;}:root { --bg-color: #ffffff; --text-color: #333333; --select-text-bg-color: #B5D6FC; --select-text-font-color: auto; --monospace: "Lucida Console",Consolas,"Courier",monospace; }
-html { font-size: 14px; background-color: var(--bg-color); color: var(--text-color); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; }
-body { margin: 0px; padding: 0px; height: auto; bottom: 0px; top: 0px; left: 0px; right: 0px; font-size: 1rem; line-height: 1.42857143; overflow-x: hidden; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; tab-size: 4; background-position: inherit inherit; background-repeat: inherit inherit; }
-iframe { margin: auto; }
-a.url { word-break: break-all; }
-a:active, a:hover { outline: 0px; }
-.in-text-selection, ::selection { text-shadow: none; background: var(--select-text-bg-color); color: var(--select-text-font-color); }
-#write { margin: 0px auto; height: auto; width: inherit; word-break: normal; word-wrap: break-word; position: relative; white-space: normal; overflow-x: visible; padding-top: 40px; }
-#write.first-line-indent p { text-indent: 2em; }
-#write.first-line-indent li p, #write.first-line-indent p * { text-indent: 0px; }
-#write.first-line-indent li { margin-left: 2em; }
-.for-image #write { padding-left: 8px; padding-right: 8px; }
-body.typora-export { padding-left: 30px; padding-right: 30px; }
-.typora-export .footnote-line, .typora-export li, .typora-export p { white-space: pre-wrap; }
-@media screen and (max-width: 500px) { 
-  body.typora-export { padding-left: 0px; padding-right: 0px; }
-  #write { padding-left: 20px; padding-right: 20px; }
-  .CodeMirror-sizer { margin-left: 0px !important; }
-  .CodeMirror-gutters { display: none !important; }
-}
-#write li > figure:last-child { margin-bottom: 0.5rem; }
-#write ol, #write ul { position: relative; }
-img { max-width: 100%; vertical-align: middle; }
-button, input, select, textarea { color: inherit; font-family: inherit; font-size: inherit; font-style: inherit; font-variant-caps: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; }
-input[type="checkbox"], input[type="radio"] { line-height: normal; padding: 0px; }
-*, ::after, ::before { box-sizing: border-box; }
-#write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p, #write pre { width: inherit; }
-#write h1, #write h2, #write h3, #write h4, #write h5, #write h6, #write p { position: relative; }
-p { line-height: inherit; }
-h1, h2, h3, h4, h5, h6 { break-after: avoid-page; break-inside: avoid; orphans: 2; }
-p { orphans: 4; }
-h1 { font-size: 2rem; }
-h2 { font-size: 1.8rem; }
-h3 { font-size: 1.6rem; }
-h4 { font-size: 1.4rem; }
-h5 { font-size: 1.2rem; }
-h6 { font-size: 1rem; }
-.md-math-block, .md-rawblock, h1, h2, h3, h4, h5, h6, p { margin-top: 1rem; margin-bottom: 1rem; }
-.hidden { display: none; }
-.md-blockmeta { color: rgb(204, 204, 204); font-weight: 700; font-style: italic; }
-a { cursor: pointer; }
-sup.md-footnote { padding: 2px 4px; background-color: rgba(238, 238, 238, 0.701961); color: rgb(85, 85, 85); border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; cursor: pointer; }
-sup.md-footnote a, sup.md-footnote a:hover { color: inherit; text-transform: inherit; text-decoration: inherit; }
-#write input[type="checkbox"] { cursor: pointer; width: inherit; height: inherit; }
-figure { overflow-x: auto; margin: 1.2em 0px; max-width: calc(100% + 16px); padding: 0px; }
-figure > table { margin: 0px !important; }
-tr { break-inside: avoid; break-after: auto; }
-thead { display: table-header-group; }
-table { border-collapse: collapse; border-spacing: 0px; width: 100%; overflow: auto; break-inside: auto; text-align: left; }
-table.md-table td { min-width: 32px; }
-.CodeMirror-gutters { border-right-width: 0px; background-color: inherit; }
-.CodeMirror-linenumber { }
-.CodeMirror { text-align: left; }
-.CodeMirror-placeholder { opacity: 0.3; }
-.CodeMirror pre { padding: 0px 4px; }
-.CodeMirror-lines { padding: 0px; }
-div.hr:focus { cursor: none; }
-#write pre { white-space: pre-wrap; }
-#write.fences-no-line-wrapping pre { white-space: pre; }
-#write pre.ty-contain-cm { white-space: normal; }
-.CodeMirror-gutters { margin-right: 4px; }
-.md-fences { font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; overflow: visible; white-space: pre; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: inherit; position: relative !important; background-position: inherit inherit; background-repeat: inherit inherit; }
-.md-diagram-panel { width: 100%; margin-top: 10px; text-align: center; padding-top: 0px; padding-bottom: 8px; overflow-x: auto; }
-#write .md-fences.mock-cm { white-space: pre-wrap; }
-.md-fences.md-fences-with-lineno { padding-left: 0px; }
-#write.fences-no-line-wrapping .md-fences.mock-cm { white-space: pre; overflow-x: auto; }
-.md-fences.mock-cm.md-fences-with-lineno { padding-left: 8px; }
-.CodeMirror-line, twitterwidget { break-inside: avoid; }
-.footnotes { opacity: 0.8; font-size: 0.9rem; margin-top: 1em; margin-bottom: 1em; }
-.footnotes + .footnotes { margin-top: 0px; }
-.md-reset { margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: top; text-decoration: none; text-shadow: none; float: none; position: static; width: auto; height: auto; white-space: nowrap; cursor: inherit; line-height: normal; font-weight: 400; text-align: left; box-sizing: content-box; direction: ltr; background-position: 0px 0px; background-repeat: initial initial; }
-li div { padding-top: 0px; }
-blockquote { margin: 1rem 0px; }
-li .mathjax-block, li p { margin: 0.5rem 0px; }
-li { margin: 0px; position: relative; }
-blockquote > :last-child { margin-bottom: 0px; }
-blockquote > :first-child, li > :first-child { margin-top: 0px; }
-.footnotes-area { color: rgb(136, 136, 136); margin-top: 0.714rem; padding-bottom: 0.143rem; white-space: normal; }
-#write .footnote-line { white-space: pre-wrap; }
-@media print { 
-  body, html { border: 1px solid transparent; height: 99%; break-after: avoid; break-before: avoid; }
-  #write { margin-top: 0px; padding-top: 0px; border-color: transparent !important; }
-  .typora-export * { -webkit-print-color-adjust: exact; }
-  html.blink-to-pdf { font-size: 13px; }
-  .typora-export #write { padding-left: 32px; padding-right: 32px; padding-bottom: 0px; break-after: avoid; }
-  .typora-export #write::after { height: 0px; }
-}
-.footnote-line { margin-top: 0.714em; font-size: 0.7em; }
-a img, img a { cursor: pointer; }
-pre.md-meta-block { font-size: 0.8rem; min-height: 0.8rem; white-space: pre-wrap; background-color: rgb(204, 204, 204); display: block; overflow-x: hidden; background-position: initial initial; background-repeat: initial initial; }
-p > .md-image:only-child:not(.md-img-error) img, p > img:only-child { display: block; margin: auto; }
-p > .md-image:only-child { display: inline-block; width: 100%; }
-#write .MathJax_Display { margin: 0.8em 0px 0px; }
-.md-math-block { width: 100%; }
-.md-math-block:not(:empty)::after { display: none; }
-[contenteditable="true"]:active, [contenteditable="true"]:focus, [contenteditable="false"]:active, [contenteditable="false"]:focus { outline: 0px; box-shadow: none; }
-.md-task-list-item { position: relative; list-style-type: none; }
-.task-list-item.md-task-list-item { padding-left: 0px; }
-.md-task-list-item > input { position: absolute; top: 0px; left: 0px; margin-left: -1.2em; margin-top: calc(1em - 10px); border: none; }
-.math { font-size: 1rem; }
-.md-toc { min-height: 3.58rem; position: relative; font-size: 0.9rem; border-top-left-radius: 10px; border-top-right-radius: 10px; border-bottom-right-radius: 10px; border-bottom-left-radius: 10px; }
-.md-toc-content { position: relative; margin-left: 0px; }
-.md-toc-content::after, .md-toc::after { display: none; }
-.md-toc-item { display: block; color: rgb(65, 131, 196); }
-.md-toc-item a { text-decoration: none; }
-.md-toc-inner:hover { text-decoration: underline; }
-.md-toc-inner { display: inline-block; cursor: pointer; }
-.md-toc-h1 .md-toc-inner { margin-left: 0px; font-weight: 700; }
-.md-toc-h2 .md-toc-inner { margin-left: 2em; }
-.md-toc-h3 .md-toc-inner { margin-left: 4em; }
-.md-toc-h4 .md-toc-inner { margin-left: 6em; }
-.md-toc-h5 .md-toc-inner { margin-left: 8em; }
-.md-toc-h6 .md-toc-inner { margin-left: 10em; }
-@media screen and (max-width: 48em) { 
-  .md-toc-h3 .md-toc-inner { margin-left: 3.5em; }
-  .md-toc-h4 .md-toc-inner { margin-left: 5em; }
-  .md-toc-h5 .md-toc-inner { margin-left: 6.5em; }
-  .md-toc-h6 .md-toc-inner { margin-left: 8em; }
-}
-a.md-toc-inner { font-size: inherit; font-style: inherit; font-weight: inherit; line-height: inherit; }
-.footnote-line a:not(.reversefootnote) { color: inherit; }
-.md-attr { display: none; }
-.md-fn-count::after { content: "."; }
-code, pre, samp, tt { font-family: var(--monospace); }
-kbd { margin: 0px 0.1em; padding: 0.1em 0.6em; font-size: 0.8em; color: rgb(36, 39, 41); background-color: rgb(255, 255, 255); border: 1px solid rgb(173, 179, 185); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; box-shadow: rgba(12, 13, 14, 0.2) 0px 1px 0px, rgb(255, 255, 255) 0px 0px 0px 2px inset; white-space: nowrap; vertical-align: middle; background-position: initial initial; background-repeat: initial initial; }
-.md-comment { color: rgb(162, 127, 3); opacity: 0.8; font-family: var(--monospace); }
-code { text-align: left; }
-a.md-print-anchor { white-space: pre !important; border: none !important; display: inline-block !important; position: absolute !important; width: 1px !important; right: 0px !important; outline: 0px !important; text-shadow: initial !important; background-position: 0px 0px !important; background-repeat: initial initial !important; }
-.md-inline-math .MathJax_SVG .noError { display: none !important; }
-.html-for-mac .inline-math-svg .MathJax_SVG { vertical-align: 0.2px; }
-.md-math-block .MathJax_SVG_Display { text-align: center; margin: 0px; position: relative; text-indent: 0px; max-width: none; max-height: none; min-height: 0px; min-width: 100%; width: auto; overflow-y: hidden; display: block !important; }
-.MathJax_SVG_Display, .md-inline-math .MathJax_SVG_Display { width: auto; margin: inherit; display: inline-block !important; }
-.MathJax_SVG .MJX-monospace { font-family: var(--monospace); }
-.MathJax_SVG .MJX-sans-serif { font-family: sans-serif; }
-.MathJax_SVG { display: inline; font-style: normal; font-weight: 400; line-height: normal; zoom: 90%; text-indent: 0px; text-align: left; text-transform: none; letter-spacing: normal; word-spacing: normal; word-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border: 0px; padding: 0px; margin: 0px; }
-.MathJax_SVG * { transition: none; }
-.MathJax_SVG_Display svg { vertical-align: middle !important; margin-bottom: 0px !important; margin-top: 0px !important; }
-.os-windows.monocolor-emoji .md-emoji { font-family: "Segoe UI Symbol", sans-serif; }
-.md-diagram-panel > svg { max-width: 100%; }
-[lang="flow"] svg, [lang="mermaid"] svg { max-width: 100%; height: auto; }
-[lang="mermaid"] .node text { font-size: 1rem; }
-table tr th { border-bottom-width: 0px; }
-video { max-width: 100%; display: block; margin: 0px auto; }
-iframe { max-width: 100%; width: 100%; border: none; }
-.highlight td, .highlight tr { border: 0px; }
-svg[id^="mermaidChart"] { line-height: 1em; }
-mark { background-color: rgb(255, 255, 0); color: rgb(0, 0, 0); background-position: initial initial; background-repeat: initial initial; }
-.md-html-inline .md-plain, .md-html-inline strong, mark .md-inline-math, mark strong { color: inherit; }
-mark .md-meta { color: rgb(0, 0, 0); opacity: 0.3 !important; }
-
-
-:root {
-    --side-bar-bg-color: #fafafa;
-    --control-text-color: #777;
-}
-
-@include-when-export url(https://fonts.loli.net/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext);
-
-html {
-    font-size: 16px;
-}
-
-body {
-    font-family: "Open Sans","Clear Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
-    color: rgb(51, 51, 51);
-    line-height: 1.6;
-}
-
-#write {
-    max-width: 860px;
-  	margin: 0 auto;
-  	padding: 30px;
-    padding-bottom: 100px;
-}
-#write > ul:first-child,
-#write > ol:first-child{
-    margin-top: 30px;
-}
-
-a {
-    color: #4183C4;
-}
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
-    position: relative;
-    margin-top: 1rem;
-    margin-bottom: 1rem;
-    font-weight: bold;
-    line-height: 1.4;
-    cursor: text;
-}
-h1:hover a.anchor,
-h2:hover a.anchor,
-h3:hover a.anchor,
-h4:hover a.anchor,
-h5:hover a.anchor,
-h6:hover a.anchor {
-    text-decoration: none;
-}
-h1 tt,
-h1 code {
-    font-size: inherit;
-}
-h2 tt,
-h2 code {
-    font-size: inherit;
-}
-h3 tt,
-h3 code {
-    font-size: inherit;
-}
-h4 tt,
-h4 code {
-    font-size: inherit;
-}
-h5 tt,
-h5 code {
-    font-size: inherit;
-}
-h6 tt,
-h6 code {
-    font-size: inherit;
-}
-h1 {
-    padding-bottom: .3em;
-    font-size: 2.25em;
-    line-height: 1.2;
-    border-bottom: 1px solid #eee;
-}
-h2 {
-   padding-bottom: .3em;
-    font-size: 1.75em;
-    line-height: 1.225;
-    border-bottom: 1px solid #eee;
-}
-h3 {
-    font-size: 1.5em;
-    line-height: 1.43;
-}
-h4 {
-    font-size: 1.25em;
-}
-h5 {
-    font-size: 1em;
-}
-h6 {
-   font-size: 1em;
-    color: #777;
-}
-p,
-blockquote,
-ul,
-ol,
-dl,
-table{
-    margin: 0.8em 0;
-}
-li>ol,
-li>ul {
-    margin: 0 0;
-}
-hr {
-    height: 2px;
-    padding: 0;
-    margin: 16px 0;
-    background-color: #e7e7e7;
-    border: 0 none;
-    overflow: hidden;
-    box-sizing: content-box;
-}
-
-li p.first {
-    display: inline-block;
-}
-ul,
-ol {
-    padding-left: 30px;
-}
-ul:first-child,
-ol:first-child {
-    margin-top: 0;
-}
-ul:last-child,
-ol:last-child {
-    margin-bottom: 0;
-}
-blockquote {
-    border-left: 4px solid #dfe2e5;
-    padding: 0 15px;
-    color: #777777;
-}
-blockquote blockquote {
-    padding-right: 0;
-}
-table {
-    padding: 0;
-    word-break: initial;
-}
-table tr {
-    border-top: 1px solid #dfe2e5;
-    margin: 0;
-    padding: 0;
-}
-table tr:nth-child(2n),
-thead {
-    background-color: #f8f8f8;
-}
-table tr th {
-    font-weight: bold;
-    border: 1px solid #dfe2e5;
-    border-bottom: 0;
-    margin: 0;
-    padding: 6px 13px;
-}
-table tr td {
-    border: 1px solid #dfe2e5;
-    margin: 0;
-    padding: 6px 13px;
-}
-table tr th:first-child,
-table tr td:first-child {
-    margin-top: 0;
-}
-table tr th:last-child,
-table tr td:last-child {
-    margin-bottom: 0;
-}
-
-.CodeMirror-lines {
-    padding-left: 4px;
-}
-
-.code-tooltip {
-    box-shadow: 0 1px 1px 0 rgba(0,28,36,.3);
-    border-top: 1px solid #eef2f2;
-}
-
-.md-fences,
-code,
-tt {
-    border: 1px solid #e7eaed;
-    background-color: #f8f8f8;
-    border-radius: 3px;
-    padding: 0;
-    padding: 2px 4px 0px 4px;
-    font-size: 0.9em;
-}
-
-code {
-    background-color: #f3f4f4;
-    padding: 0 2px 0 2px;
-}
-
-.md-fences {
-    margin-bottom: 15px;
-    margin-top: 15px;
-    padding-top: 8px;
-    padding-bottom: 6px;
-}
-
-
-.md-task-list-item > input {
-  margin-left: -1.3em;
-}
-
-@media print {
-    html {
-        font-size: 13px;
-    }
-    table,
-    pre {
-        page-break-inside: avoid;
-    }
-    pre {
-        word-wrap: break-word;
-    }
-}
-
-.md-fences {
-	background-color: #f8f8f8;
-}
-#write pre.md-meta-block {
-	padding: 1rem;
-    font-size: 85%;
-    line-height: 1.45;
-    background-color: #f7f7f7;
-    border: 0;
-    border-radius: 3px;
-    color: #777777;
-    margin-top: 0 !important;
-}
-
-.mathjax-block>.code-tooltip {
-	bottom: .375rem;
-}
-
-.md-mathjax-midline {
-    background: #fafafa;
-}
-
-#write>h3.md-focus:before{
-	left: -1.5625rem;
-	top: .375rem;
-}
-#write>h4.md-focus:before{
-	left: -1.5625rem;
-	top: .285714286rem;
-}
-#write>h5.md-focus:before{
-	left: -1.5625rem;
-	top: .285714286rem;
-}
-#write>h6.md-focus:before{
-	left: -1.5625rem;
-	top: .285714286rem;
-}
-.md-image>.md-meta {
-    /*border: 1px solid #ddd;*/
-    border-radius: 3px;
-    padding: 2px 0px 0px 4px;
-    font-size: 0.9em;
-    color: inherit;
-}
-
-.md-tag {
-    color: #a7a7a7;
-    opacity: 1;
-}
-
-.md-toc { 
-    margin-top:20px;
-    padding-bottom:20px;
-}
-
-.sidebar-tabs {
-    border-bottom: none;
-}
-
-#typora-quick-open {
-    border: 1px solid #ddd;
-    background-color: #f8f8f8;
-}
-
-#typora-quick-open-item {
-    background-color: #FAFAFA;
-    border-color: #FEFEFE #e5e5e5 #e5e5e5 #eee;
-    border-style: solid;
-    border-width: 1px;
-}
-
-/** focus mode */
-.on-focus-mode blockquote {
-    border-left-color: rgba(85, 85, 85, 0.12);
-}
-
-header, .context-menu, .megamenu-content, footer{
-    font-family: "Segoe UI", "Arial", sans-serif;
-}
-
-.file-node-content:hover .file-node-icon,
-.file-node-content:hover .file-node-open-state{
-    visibility: visible;
-}
-
-.mac-seamless-mode #typora-sidebar {
-    background-color: #fafafa;
-    background-color: var(--side-bar-bg-color);
-}
-
-.md-lang {
-    color: #b4654d;
-}
-
-.html-for-mac .context-menu {
-    --item-hover-bg-color: #E6F0FE;
-}
-
-#md-notification .btn {
-    border: 0;
-}
-
-.dropdown-menu .divider {
-    border-color: #e5e5e5;
-}
-
-.ty-preferences .window-content {
-    background-color: #fafafa;
-}
-
-.ty-preferences .nav-group-item.active {
-    color: white;
-    background: #999;
-}
-
- .typora-export li, .typora-export p, .typora-export,  .footnote-line {white-space: normal;} 
-</style>
-</head>
-<body class='typora-export' >
-<div  id='write'  class = 'is-mac'><p align="center">
-  <a href="https://github.com/geekidea/spring-boot-plus">
-   <img alt="spring-boot-plus logo" src="https://springboot.plus/img/logo.png">
-  </a>
-</p>
-<p align="center">
-  Everyone can develop projects independently, quickly and efficiently！
-</p>
-<p align="center">  
-  <a href="https://github.com/geekidea/spring-boot-plus/">
-    <img alt="spring-boot-plus version" src="https://img.shields.io/badge/spring--boot--plus-2.0-blue">
-  </a>
-  <a href="https://github.com/spring-projects/spring-boot">
-    <img alt="spring boot version" src="https://img.shields.io/badge/spring%20boot-2.2.5.RELEASE-brightgreen">
-  </a>
-  <a href="https://www.apache.org/licenses/LICENSE-2.0">
-    <img alt="code style" src="https://img.shields.io/badge/license-Apache%202-4EB1BA.svg?style=flat-square">
-  </a>
-</p><p>&nbsp;</p><h3><a name="spring-boot-plus是前后端分离的快速开发脚手架" class="md-header-anchor"></a><span>spring-boot-plus是前后端分离的快速开发脚手架</span></h3><blockquote><p><span>Spring-Boot-Plus是易于使用，快速，高效，功能丰富，开源的spring boot 脚手架.</span></p></blockquote><h2><a name="目标" class="md-header-anchor"></a><span>目标</span></h2><blockquote><p><span>每个人都可以独立、快速、高效地开发项目！</span></p></blockquote><h2><a name="版本库" class="md-header-anchor"></a><span>版本库</span></h2><h4><a name="githubhttpsgithubcomgeekideaspring-boot-plus--giteehttpsgiteecomgeekideaspring-boot-plus" class="md-header-anchor"></a><a href='https://github.com/geekidea/spring-boot-plus'><span>GITHUB</span></a><span> | </span><a href='https://gitee.com/geekidea/spring-boot-plus'><span>GITEE</span></a></h4><h2><a name="官网" class="md-header-anchor"></a><span>官网</span></h2><h4><a name="springbootplushttpspringbootplus" class="md-header-anchor"></a><a href='http://springboot.plus'><span>springboot.plus</span></a></h4><h3><a name="主要特性" class="md-header-anchor"></a><span>主要特性</span></h3><ol start='' ><li><span>集成spring boot 常用开发组件集、公共配置、AOP日志等</span></li><li><span>集成mybatis plus快速dao操作</span></li><li><span>快速生成后台代码: entity/param/vo/controller/service/mapper/xml</span></li><li><span>集成swagger2，可自动生成api文档</span></li><li><span>集成jwt、shiro/spring security权限控制</span></li><li><span>集成redis、spring cache、ehcache缓存</span></li><li><span>集成rabbit/rocket/kafka mq消息队列</span></li><li><span>集成druid连接池，JDBC性能和慢查询检测</span></li><li><span>集成spring boot admin，实时检测项目运行情况</span></li><li><span>使用assembly maven插件进行不同环境打包部署,包含启动、重启命令，配置文件提取到外部config目录</span></li></ol><h2><a name="联系" class="md-header-anchor"></a><span>联系</span></h2><ul><li><span>spring-boot-plus技术交流群</span></li></ul><p><img src="https://spring-boot-plus.gitee.io/img/spring-boot-plus-qq-group.png" referrerpolicy="no-referrer" alt="spring-boot-plus QQ Group"></p><h2><a name="license" class="md-header-anchor"></a><span>License</span></h2><p><span>spring-boot-plus is under the Apache 2.0 license. See the </span><a href='https://github.com/geekidea/spring-boot-plus/blob/master/LICENSE'><span>LICENSE</span></a><span> file for details.</span></p><p>&nbsp;</p></div>
-</body>
-</html>
\ No newline at end of file
diff --git a/config/src/main/resources/static/verifyCode.html b/config/src/main/resources/static/verifyCode.html
deleted file mode 100644
index 7d93f8b2..00000000
--- a/config/src/main/resources/static/verifyCode.html
+++ /dev/null
@@ -1,64 +0,0 @@
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <title>验证码</title>
-</head>
-<body>
-
-<div>
-    <p>方式一：获取图片</p>
-    <img id="imageCode" src="http://localhost:8888/verificationCode/getImage" alt="" onclick="changeImage()">
-    <p>verifyToken：查看Responses Headers</p>
-</div>
-
-<hr>
-
-<div>
-    <p>方式二：获取base64图片编码</p>
-    <img src="" alt="" id="base64ImageCode" onclick="changeBase64Image()">
-    <p id="verifyToken"></p>
-</div>
-
-<script type="text/javascript">
-    function changeImage() {
-        document.getElementById('imageCode').src = "http://localhost:8888/api/verificationCode/getImage?time=" + new Date().getTime();
-    }
-
-    var url = "http://localhost:8888/api/verificationCode/getBase64Image";
-
-    function changeBase64Image() {
-        var  xhr = new XMLHttpRequest();
-        xhr.onreadystatechange = function () {
-            if (xhr.readyState == 4 && xhr.status == 200) {
-                var result = JSON.parse(xhr.responseText);
-                document.getElementById('base64ImageCode').src = result.data.image;
-                document.getElementById('verifyToken').innerText = "verifyToken：" + result.data.verifyToken;
-            }
-        };
-        xhr.open("GET", url, true);
-        xhr.send(null);
-    }
-
-    changeBase64Image();
-
-</script>
-
-</body>
-</html>
\ No newline at end of file
diff --git a/config/src/main/resources/static/welcome.html b/config/src/main/resources/static/welcome.html
deleted file mode 100644
index a32de337..00000000
--- a/config/src/main/resources/static/welcome.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <title>Welcome to spring-boot-plus</title>
-</head>
-<body>
-<h1>Welcome to spring-boot-plus</h1>
-<h3>
-    <a href="https://github.com/geekidea/spring-boot-plus" target="_blank">GITHUB</a>
-</h3>
-<h3>
-    <a href="https://gitee.com/geekidea/spring-boot-plus" target="_blank">GITEE</a>
-</h3>
-<h2>
-    <a href="https://springboot.plus" target="_blank">springboot.plus</a>
-</h2>
-</body>
-</html>
\ No newline at end of file
diff --git a/db/mysql_spring_boot_plus.sql b/db/mysql_spring_boot_plus.sql
new file mode 100644
index 00000000..9c612af2
--- /dev/null
+++ b/db/mysql_spring_boot_plus.sql
@@ -0,0 +1,748 @@
+-- ----------------------------
+-- Table structure for foo_bar
+-- ----------------------------
+DROP TABLE IF EXISTS foo_bar;
+CREATE TABLE foo_bar
+(
+    id          bigint(0)   NOT NULL COMMENT '主键',
+    name        varchar(20) NOT NULL COMMENT '名称',
+    foo         varchar(100)         DEFAULT NULL COMMENT 'Foo',
+    bar         varchar(100)         DEFAULT NULL COMMENT 'Bar',
+    remark      varchar(200)         DEFAULT NULL COMMENT '备注',
+    status      tinyint(1)  NOT NULL DEFAULT 1 COMMENT '状态，0：禁用，1：启用',
+    create_id   bigint(0)            DEFAULT NULL COMMENT '创建人ID',
+    create_time datetime(0)          DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id   bigint(0)            DEFAULT NULL COMMENT '修改人ID',
+    update_time datetime(0)          DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = 'FooBar'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Records of foo_bar
+-- ----------------------------
+INSERT INTO foo_bar
+VALUES (1, 'FooBar', 'Foo', 'Bar', NULL, 1, 1, '2023-07-01 21:01:10', NULL, NULL);
+
+-- ----------------------------
+-- Table structure for generator_column
+-- ----------------------------
+DROP TABLE IF EXISTS generator_column;
+CREATE TABLE generator_column
+(
+    id                    bigint(0)    NOT NULL COMMENT '主键',
+    table_name            varchar(200) NOT NULL COMMENT '表名称',
+    column_name           varchar(200) NOT NULL COMMENT '字段名称',
+    column_comment        varchar(200)          DEFAULT NULL COMMENT '字段注释',
+    column_custom_comment varchar(200)          DEFAULT NULL COMMENT '自定义注释',
+    data_type             varchar(100)          DEFAULT NULL COMMENT '数据类型',
+    column_type           varchar(100)          DEFAULT NULL COMMENT '字段类型',
+    column_length         int(0)                DEFAULT NULL COMMENT '列长度',
+    exists_length         tinyint(1)   NOT NULL DEFAULT 0 COMMENT '是否存在长度',
+    column_sort           int(0)       NOT NULL DEFAULT 0 COMMENT '列顺序',
+    property_name         varchar(200) NOT NULL COMMENT '字段名称',
+    property_type         varchar(100)          DEFAULT NULL COMMENT '属性类型',
+    is_pk                 tinyint(1)   NOT NULL DEFAULT 0 COMMENT '是否主键',
+    is_required           tinyint(1)   NOT NULL DEFAULT 0 COMMENT '是否必填',
+    is_default_value      tinyint(1)   NOT NULL DEFAULT 0 COMMENT '是否有默认值 1：有，0：无',
+    is_validate           tinyint(1)   NOT NULL DEFAULT 0 COMMENT '是否校验 0：不校验，1：校验',
+    is_form               tinyint(1)   NOT NULL DEFAULT 0 COMMENT '是否表单字段',
+    is_list               tinyint(1)   NOT NULL DEFAULT 0 COMMENT '是否列表字段',
+    is_query              tinyint(1)   NOT NULL DEFAULT 0 COMMENT '是否条件查询',
+    query_type            tinyint(0)            DEFAULT NULL COMMENT '查询方式 1：等于，2：不等于，3：大于，4：大于等于，5：小于，6：小于等于，7：like，8：日期范围，9：日期时间范围',
+    form_type             tinyint(0)            DEFAULT NULL COMMENT '表单类型 1：单行文本，2：多行文本，3：数字框，4：单选框，5：复选框，6：下拉框，7：日期，8：日期时间，9：时间，10：文件上传，11：富文本',
+    dict_type             varchar(200)          DEFAULT NULL COMMENT '字典类型编码',
+    option_json           varchar(1000)         DEFAULT NULL COMMENT '枚举类型json',
+    create_id             bigint(0)             DEFAULT NULL COMMENT '创建人ID',
+    create_time           datetime(0)           DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id             bigint(0)             DEFAULT NULL COMMENT '修改人ID',
+    update_time           datetime(0)           DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '生成代码列'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for generator_table
+-- ----------------------------
+DROP TABLE IF EXISTS generator_table;
+CREATE TABLE generator_table
+(
+    id                        bigint(0)    NOT NULL COMMENT '主键',
+    table_name                varchar(200) NOT NULL COMMENT '表名称',
+    table_comment             varchar(200)          DEFAULT NULL COMMENT '表注释',
+    class_name                varchar(200)          DEFAULT NULL COMMENT '类名称',
+    module_name               varchar(100)          DEFAULT NULL COMMENT '模块名称',
+    package_name              varchar(200)          DEFAULT NULL COMMENT '包名称',
+    author                    varchar(100)          DEFAULT NULL COMMENT '作者',
+    id_type                   varchar(32)           DEFAULT NULL COMMENT '生成ID类型枚举',
+    generator_backend         tinyint(1)   NOT NULL DEFAULT 1 COMMENT '是否生成后端 1：是，0：否',
+    generator_app_backend     tinyint(1)            DEFAULT 0 COMMENT '是否生成App后端代码 1：是，0：否',
+    generator_frontend        tinyint(1)   NOT NULL DEFAULT 1 COMMENT '是否生成前端 1：是，0：否',
+    validate_field            tinyint(1)   NOT NULL DEFAULT 0 COMMENT '是否校验字段 1：是，0：否',
+    enable_log                tinyint(1)   NOT NULL DEFAULT 1 COMMENT '是否启用日志注解  1：是，0：否',
+    enable_permission         tinyint(1)   NOT NULL DEFAULT 1 COMMENT '是否启用权限编码 1：是，0：否',
+    request_mapping_style     int(0)       NOT NULL DEFAULT 1 COMMENT '请求映射风格 1：默认，2：restful，3：全部小写，4：中横线，5：下划线',
+    default_order_column_name varchar(100)          DEFAULT NULL COMMENT '默认排序列名称',
+    parent_menu_id            bigint(0)             DEFAULT NULL COMMENT '上级菜单ID',
+    form_layout               tinyint(0)   NOT NULL DEFAULT 2 COMMENT '表单布局方式 1：一行一列，2：一行两列',
+    generator_type            tinyint(0)   NOT NULL DEFAULT 1 COMMENT '生成方式 1：zip压缩包，2：自定义路径',
+    custom_backend_path       varchar(300)          DEFAULT NULL COMMENT '自定义生成后端路径',
+    custom_frontend_path      varchar(300)          DEFAULT NULL COMMENT '自定义生成前端路径',
+    show_default_query        tinyint(1)            DEFAULT NULL COMMENT '是否显示默认查询条件 1：是，0：否',
+    only_generator_entity     tinyint(1)            DEFAULT NULL COMMENT '是否只生成实体类 1：是，0：否',
+    create_id                 bigint(0)             DEFAULT NULL COMMENT '创建人ID',
+    create_time               datetime(0)           DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id                 bigint(0)             DEFAULT NULL COMMENT '修改人ID',
+    update_time               datetime(0)           DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE,
+    UNIQUE INDEX generator_table_name (table_name) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '生成代码表'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_config
+-- ----------------------------
+DROP TABLE IF EXISTS sys_config;
+CREATE TABLE sys_config
+(
+    id           bigint(0)    NOT NULL COMMENT '主键',
+    config_name  varchar(100) NOT NULL COMMENT '配置名称',
+    config_key   varchar(100) NOT NULL COMMENT '配置key',
+    config_value varchar(200) NOT NULL COMMENT '配置值',
+    is_system    tinyint(1)   NOT NULL DEFAULT 0 COMMENT '是否是系统字典类型',
+    status       tinyint(1)   NOT NULL DEFAULT 1 COMMENT '状态 1：正常，0：禁用',
+    remark       varchar(200)          DEFAULT NULL COMMENT '备注',
+    create_id    bigint(0)             DEFAULT NULL COMMENT '创建人ID',
+    update_id    bigint(0)             DEFAULT NULL COMMENT '修改人ID',
+    create_time  datetime(0)           DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_time  datetime(0)           DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE,
+    UNIQUE INDEX sys_config_config_key_uindex (config_key) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '系统配置'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_dict
+-- ----------------------------
+DROP TABLE IF EXISTS sys_dict;
+CREATE TABLE sys_dict
+(
+    id          bigint(0)    NOT NULL COMMENT '主键',
+    value       varchar(200) NOT NULL COMMENT '字典值',
+    label       varchar(100) NOT NULL COMMENT '字典名称',
+    dict_code   varchar(100) NOT NULL COMMENT '字典类型code',
+    status      tinyint(1)   NOT NULL DEFAULT 1 COMMENT '状态 1：启用，0：禁用',
+    sort        int(0)       NOT NULL DEFAULT 0 COMMENT '排序值',
+    remark      varchar(200)          DEFAULT NULL COMMENT '备注',
+    create_id   bigint(0)             DEFAULT NULL COMMENT '创建人ID',
+    create_time datetime(0)           DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id   bigint(0)             DEFAULT NULL COMMENT '修改人ID',
+    update_time datetime(0)           DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE,
+    UNIQUE INDEX sys_dict_dict_code_value_uindex (dict_code, value) USING BTREE,
+    INDEX sys_dict_dict_type_index (dict_code) USING BTREE,
+    INDEX sys_dict_label_index (label) USING BTREE,
+    INDEX sys_dict_value_index (value) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '字典数据'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Records of sys_dict
+-- ----------------------------
+INSERT INTO sys_dict
+VALUES (489964068171781, '1', '1', '489941097336837', 1, 1, '1', 1, '2023-12-05 14:08:55', NULL, NULL);
+INSERT INTO sys_dict
+VALUES (489964253274117, '2', '2', '489941097336837', 1, 2, '2', 1, '2023-12-05 14:09:41', NULL, NULL);
+INSERT INTO sys_dict
+VALUES (489964289384453, '3', '3', '489941097336837', 1, 3, '3', 1, '2023-12-05 14:09:49', NULL, NULL);
+INSERT INTO sys_dict
+VALUES (489964579540997, '4', '4', '489941097336837', 1, 4, '4', 1, '2023-12-05 14:11:00', NULL, NULL);
+INSERT INTO sys_dict
+VALUES (489964663173125, '5', '5', '489941097336837', 1, 5, '5', 1, '2023-12-05 14:11:21', NULL, NULL);
+INSERT INTO sys_dict
+VALUES (489964722814981, '6', '6', '489941097336837', 1, 6, '6', 1, '2023-12-05 14:11:35', NULL, NULL);
+INSERT INTO sys_dict
+VALUES (489965510180869, '7', '7', '489941097336837', 1, 7, '7', 1, '2023-12-05 14:14:47', NULL, NULL);
+INSERT INTO sys_dict
+VALUES (489965622493189, '8', '8', '489941097336837', 1, 8, '8', 1, '2023-12-05 14:15:15', NULL, NULL);
+INSERT INTO sys_dict
+VALUES (489965709664261, '9', '9', '489941097336837', 1, 9, '9', 1, '2023-12-05 14:15:36', NULL, NULL);
+INSERT INTO sys_dict
+VALUES (489965730222085, '102', '102', '489941097336837', 0, 102, '102', 1, '2023-12-05 14:15:41', NULL,
+        '2023-12-05 14:48:20');
+INSERT INTO sys_dict
+VALUES (490997344141317, '1', '1', '1', 1, 2, '1', 1, '2023-12-08 12:13:20', NULL, NULL);
+INSERT INTO sys_dict
+VALUES (491478883053573, 'zfb', '支付宝', '491478519701509', 1, 111, '11111111111111', 1, '2023-12-09 20:52:43', NULL,
+        NULL);
+INSERT INTO sys_dict
+VALUES (492217300738053, 'test', '测试', 'test', 1, 0, 'cs', 1, '2023-12-11 22:57:21', 1, '2024-01-12 23:16:21');
+INSERT INTO sys_dict
+VALUES (498426711265285, 'test1', '测试1', 'test_dict_type', 1, 0, NULL, 1, '2023-12-29 12:03:31', NULL, NULL);
+
+-- ----------------------------
+-- Table structure for sys_dict_type
+-- ----------------------------
+DROP TABLE IF EXISTS sys_dict_type;
+CREATE TABLE sys_dict_type
+(
+    id          bigint(0)    NOT NULL COMMENT '主键',
+    code        varchar(100) NOT NULL COMMENT '字典类型编码',
+    name        varchar(100) NOT NULL COMMENT '字典类型名称',
+    is_system   tinyint(1)   NOT NULL DEFAULT 0 COMMENT '是否是系统字典类型',
+    remark      varchar(200)          DEFAULT NULL COMMENT '备注',
+    create_id   bigint(0)             DEFAULT NULL COMMENT '创建人ID',
+    create_time datetime(0)           DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id   bigint(0)             DEFAULT NULL COMMENT '修改人ID',
+    update_time datetime(0)           DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE,
+    UNIQUE INDEX sys_dict_type_code_uindex (code) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '字典类型'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Records of sys_dict_type
+-- ----------------------------
+INSERT INTO sys_dict_type
+VALUES (1, 'vip_level', 'VIP等级11', 1, '会员等级 1：普通会员，2：黄金会员，3：铂金会员，4：钻石会员', 1, '2023-11-25 22:17:51', NULL,
+        '2023-12-09 20:47:48');
+INSERT INTO sys_dict_type
+VALUES (489941097336837, 'test', '测试', 1, '测试', 1, '2023-12-05 12:35:27', NULL, NULL);
+INSERT INTO sys_dict_type
+VALUES (491478519701509, 'pay_way11', '支付方式111', 1, '支付方式备注。。。1111', 1, '2023-12-09 20:51:15', NULL,
+        '2023-12-09 20:51:54');
+INSERT INTO sys_dict_type
+VALUES (492524988080133, 'qqq', '1', 1, 'qqqqqqqqqq', 1, '2023-12-12 19:49:20', NULL, NULL);
+
+-- ----------------------------
+-- Table structure for sys_file
+-- ----------------------------
+DROP TABLE IF EXISTS sys_file;
+CREATE TABLE sys_file
+(
+    id                 bigint(0)  NOT NULL COMMENT '主键',
+    trace_id           varchar(32)    DEFAULT NULL COMMENT '日志链路ID',
+    server_type        tinyint(0) NOT NULL COMMENT '服务类型 1：本地服务，2：阿里云OSS',
+    upload_type        varchar(32)    DEFAULT NULL COMMENT '上传类型',
+    dir_name           varchar(100)   DEFAULT NULL COMMENT '目录名称',
+    original_file_name varchar(200)   DEFAULT NULL COMMENT '源文件名称',
+    file_name          varchar(200)   DEFAULT NULL COMMENT '生成的文件名称',
+    content_type       varchar(200)   DEFAULT NULL COMMENT '文件内容类型',
+    extension          varchar(32)    DEFAULT NULL COMMENT '文件后缀',
+    size               bigint(0)      DEFAULT NULL COMMENT '文件大小',
+    size_mb            decimal(10, 2) DEFAULT NULL COMMENT '文件大小MB',
+    url                varchar(500)   DEFAULT NULL COMMENT '访问的URL',
+    system_type        tinyint(0)     DEFAULT NULL COMMENT '系统类型 1：管理端，2：移动端',
+    user_id            bigint(0)      DEFAULT NULL COMMENT '用户ID',
+    file_path          varchar(500)   DEFAULT NULL COMMENT '本地文件服务时的物流文件位置',
+    file_type          int(0)         DEFAULT NULL COMMENT '文件类型 1：图片，2：音视频，3：文档，4：文件',
+    ip                 varchar(32)    DEFAULT NULL COMMENT 'IP地址',
+    ip_area            varchar(100)   DEFAULT NULL COMMENT 'IP区域',
+    create_id          bigint(0)      DEFAULT NULL COMMENT '创建人ID',
+    create_time        datetime(0)    DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id          bigint(0)      DEFAULT NULL COMMENT '修改人ID',
+    update_time        datetime(0)    DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '系统文件'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Records of sys_file
+-- ----------------------------
+INSERT INTO sys_file
+VALUES (492564876013573, '492564820004869', 2, 'any', '202312', 'logo.png', '20231212223125492564821676037.png',
+        'image/png', 'png', 8046, 0.01,
+        'https://geekidea.oss-cn-chengdu.aliyuncs.com/boot/202312/20231212223125492564821676037.png', 1, 1, NULL, 1,
+        '127.0.0.1', '内网IP', 1, '2023-12-12 22:31:38', NULL, NULL);
+INSERT INTO sys_file
+VALUES (507346623787013, '507346619826181', 2, 'any', '202401', '144.png', '20240123165843507346622103557.png',
+        'image/png', 'png', 4288, 0.00,
+        'https://geekidea.oss-cn-chengdu.aliyuncs.com/boot/202401/20240123165843507346622103557.png', 1, 1, NULL, 1,
+        '127.0.0.1', '内网IP', 1, '2024-01-23 16:58:44', NULL, '2024-01-23 16:58:44');
+
+-- ----------------------------
+-- Table structure for sys_log
+-- ----------------------------
+DROP TABLE IF EXISTS sys_log;
+CREATE TABLE sys_log
+(
+    id                bigint(0) NOT NULL COMMENT '主键',
+    trace_id          varchar(36)        DEFAULT NULL COMMENT '日志链路ID',
+    request_time      varchar(30)        DEFAULT NULL COMMENT '请求时间',
+    request_url       varchar(1000)      DEFAULT NULL COMMENT '全路径',
+    permission_code   varchar(200)       DEFAULT NULL COMMENT '权限编码',
+    log_name          varchar(200)       DEFAULT NULL COMMENT '日志名称',
+    request_method    varchar(10)        DEFAULT NULL COMMENT '请求方式，GET/POST',
+    content_type      varchar(200)       DEFAULT NULL COMMENT '内容类型',
+    is_request_body   tinyint(1)         DEFAULT NULL COMMENT '是否是JSON请求映射参数',
+    token             varchar(100)       DEFAULT NULL COMMENT 'token',
+    module_name       varchar(100)       DEFAULT NULL COMMENT '模块名称',
+    class_name        varchar(200)       DEFAULT NULL COMMENT 'controller类名称',
+    method_name       varchar(200)       DEFAULT NULL COMMENT 'controller方法名称',
+    request_param     text COMMENT '请求参数',
+    system_type       int(0)             DEFAULT NULL COMMENT '系统类型 1：Admin管理后台，2：APP移动端端',
+    user_id           bigint(0)          DEFAULT NULL COMMENT '用户ID',
+    request_ip        varchar(15)        DEFAULT NULL COMMENT '请求ip',
+    ip_country        varchar(100)       DEFAULT NULL COMMENT 'IP国家',
+    ip_province       varchar(100)       DEFAULT NULL COMMENT 'IP省份',
+    ip_city           varchar(100)       DEFAULT NULL COMMENT 'IP城市',
+    ip_area_desc      varchar(100)       DEFAULT NULL COMMENT 'IP区域描述',
+    ip_isp            varchar(100)       DEFAULT NULL COMMENT 'IP运营商',
+    log_type          int(0)    NOT NULL DEFAULT 0 COMMENT '0:其它,1:新增,2:修改,3:删除,4:详情查询,5:所有列表,6:分页列表,7:其它查询,8:上传文件',
+    response_time     varchar(100)       DEFAULT NULL COMMENT '响应时间',
+    response_success  tinyint(1)         DEFAULT NULL COMMENT '0:失败,1:成功',
+    response_code     int(0)             DEFAULT NULL COMMENT '响应结果状态码',
+    response_message  text COMMENT '响应结果消息',
+    response_data     text COMMENT '响应数据',
+    exception_name    varchar(200)       DEFAULT NULL COMMENT '异常类名称',
+    exception_message text COMMENT '异常信息',
+    diff_time         bigint(0)          DEFAULT NULL COMMENT '耗时，单位：毫秒',
+    diff_time_desc    varchar(100)       DEFAULT NULL COMMENT '耗时描述',
+    referer           varchar(1000)      DEFAULT NULL COMMENT '请求来源地址',
+    origin            varchar(1000)      DEFAULT NULL COMMENT '请求来源服务名',
+    source_type       varchar(100)       DEFAULT NULL COMMENT '请求来源类型',
+    is_mobile         tinyint(1)         DEFAULT NULL COMMENT '是否手机 0：否，1：是',
+    platform_name     varchar(100)       DEFAULT NULL COMMENT '平台名称',
+    browser_name      varchar(100)       DEFAULT NULL COMMENT '浏览器名称',
+    user_agent        varchar(1000)      DEFAULT NULL COMMENT '用户环境',
+    remark            varchar(200)       DEFAULT NULL COMMENT '备注',
+    create_id         bigint(0)          DEFAULT NULL COMMENT '创建人ID',
+    create_time       datetime(0)        DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id         bigint(0)          DEFAULT NULL COMMENT '修改人ID',
+    update_time       datetime(0)        DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE,
+    INDEX sys_log_log_name_index (log_name) USING BTREE,
+    INDEX sys_log_log_type_index (log_type) USING BTREE,
+    INDEX sys_log_module_name_index (module_name) USING BTREE,
+    INDEX sys_log_permission_code_index (permission_code) USING BTREE,
+    INDEX sys_log_request_ip_index (request_ip) USING BTREE,
+    INDEX sys_log_response_success_index (response_success) USING BTREE,
+    INDEX sys_log_trace_id_index (trace_id) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '系统日志'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_login_log
+-- ----------------------------
+DROP TABLE IF EXISTS sys_login_log;
+CREATE TABLE sys_login_log
+(
+    id                bigint(0)  NOT NULL COMMENT '主键',
+    request_id        varchar(32)         DEFAULT NULL COMMENT '请求ID',
+    username          varchar(32)         DEFAULT NULL COMMENT '用户名称',
+    ip                varchar(15)         DEFAULT NULL COMMENT 'IP',
+    token             varchar(32)         DEFAULT NULL COMMENT 'token',
+    type              int(0)              DEFAULT NULL COMMENT '1:登录，2：登出',
+    success           tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否成功 true:成功/false:失败',
+    code              int(0)              DEFAULT NULL COMMENT '响应码',
+    exception_message varchar(300)        DEFAULT NULL COMMENT '失败消息记录',
+    user_agent        varchar(300)        DEFAULT NULL COMMENT '用户环境',
+    browser_name      varchar(100)        DEFAULT NULL COMMENT '浏览器名称',
+    browser_version   varchar(100)        DEFAULT NULL COMMENT '浏览器版本',
+    engine_name       varchar(100)        DEFAULT NULL COMMENT '浏览器引擎名称',
+    engine_version    varchar(100)        DEFAULT NULL COMMENT '浏览器引擎版本',
+    os_name           varchar(100)        DEFAULT NULL COMMENT '系统名称',
+    platform_name     varchar(100)        DEFAULT NULL COMMENT '平台名称',
+    mobile            tinyint(1)          DEFAULT NULL COMMENT '是否是手机,0:否,1:是',
+    device_model      varchar(100)        DEFAULT NULL COMMENT '移动端设备型号',
+    remark            varchar(200)        DEFAULT NULL COMMENT '备注',
+    create_id         bigint(0)           DEFAULT NULL COMMENT '创建人ID',
+    create_time       datetime(0)         DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id         bigint(0)           DEFAULT NULL COMMENT '修改人ID',
+    update_time       datetime(0)         DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '系统登录日志'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for sys_menu
+-- ----------------------------
+DROP TABLE IF EXISTS sys_menu;
+CREATE TABLE sys_menu
+(
+    id             bigint(0)  NOT NULL COMMENT '主键',
+    name           varchar(32)         DEFAULT NULL COMMENT '菜单名称',
+    parent_id      bigint(0)  NOT NULL COMMENT '父id',
+    type           tinyint(0) NOT NULL COMMENT '菜单类型，1：目录，2：菜单，3：权限',
+    code           varchar(100)        DEFAULT NULL COMMENT '菜单编码',
+    route_url      varchar(200)        DEFAULT NULL COMMENT '前端路由地址',
+    component_path varchar(100)        DEFAULT NULL COMMENT '组件路径',
+    route_redirect varchar(100)        DEFAULT NULL COMMENT '重定向',
+    icon           varchar(100)        DEFAULT NULL COMMENT '菜单图标',
+    sort           int(0)     NOT NULL DEFAULT 0 COMMENT '排序',
+    is_show        tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否显示,0：不显示，1：显示',
+    is_cache       tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否缓存，0：否 1：是',
+    status         tinyint(1) NOT NULL DEFAULT 1 COMMENT '状态，0：禁用，1：启用',
+    create_id      bigint(0)           DEFAULT NULL COMMENT '创建人ID',
+    create_time    datetime(0)         DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id      bigint(0)           DEFAULT NULL COMMENT '修改人ID',
+    update_time    datetime(0)         DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE,
+    INDEX sys_menu_name_index (name) USING BTREE,
+    INDEX sys_menu_status_index (status) USING BTREE,
+    INDEX sys_menu_type_index (type) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '系统菜单'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Records of sys_menu
+-- ----------------------------
+INSERT INTO sys_menu
+VALUES (11, '系统管理', 0, 1, '', '/system', '', '', 'ele-setting', 99, 1, 0, 1, 1, '2023-05-27 12:02:42', 1,
+        '2023-12-25 12:26:10');
+INSERT INTO sys_menu
+VALUES (1101, '用户管理', 11, 2, 'sys:user:list', '/system/user', 'system/user/index', NULL, 'ele-user', 1, 1, 0, 1, 1,
+        '2023-05-27 12:04:05', NULL, '2023-06-16 09:08:20');
+INSERT INTO sys_menu
+VALUES (1102, '角色管理', 11, 2, 'sys:role:list', '/system/role', 'system/role/index', NULL, 'local-role', 2, 1, 1, 1, 1,
+        '2023-06-18 08:02:48', NULL, NULL);
+INSERT INTO sys_menu
+VALUES (1103, '菜单管理', 11, 2, 'sys:menu:tree-list', '/system/menu', 'system/menu/index', NULL, 'local-menu', 3, 1, 1, 1,
+        1, '2023-06-18 08:03:32', NULL, '2023-12-04 17:35:25');
+INSERT INTO sys_menu
+VALUES (110101, '添加用户', 1101, 3, 'sys:user:add', NULL, NULL, NULL, NULL, 1, 1, 0, 1, 1, '2023-06-18 20:22:12', NULL,
+        NULL);
+INSERT INTO sys_menu
+VALUES (110102, '修改用户', 1101, 3, 'sys:user:update', NULL, NULL, NULL, NULL, 2, 1, 0, 1, 1, '2023-06-18 20:22:12', NULL,
+        NULL);
+INSERT INTO sys_menu
+VALUES (110103, '删除用户', 1101, 3, 'sys:user:delete', NULL, NULL, NULL, NULL, 3, 1, 0, 1, 1, '2023-06-18 20:22:12', NULL,
+        NULL);
+INSERT INTO sys_menu
+VALUES (110104, '用户详情', 1101, 3, 'sys:user:info', NULL, NULL, NULL, NULL, 4, 1, 0, 1, 1, '2023-06-18 20:22:12', NULL,
+        NULL);
+INSERT INTO sys_menu
+VALUES (110106, '重置用户密码', 1101, 3, 'sys:user:reset-password', NULL, NULL, NULL, NULL, 6, 1, 0, 1, 1,
+        '2023-06-18 20:22:12', NULL, NULL);
+INSERT INTO sys_menu
+VALUES (110201, '添加角色', 1102, 3, 'sys:role:add', NULL, NULL, NULL, NULL, 1, 1, 0, 1, 1, '2023-06-18 20:22:12', NULL,
+        NULL);
+INSERT INTO sys_menu
+VALUES (110202, '修改角色', 1102, 3, 'sys:role:update', NULL, NULL, NULL, NULL, 2, 1, 0, 1, 1, '2023-06-18 20:22:12', NULL,
+        NULL);
+INSERT INTO sys_menu
+VALUES (110203, '删除角色', 1102, 3, 'sys:role:delete', NULL, NULL, NULL, NULL, 3, 1, 0, 1, 1, '2023-06-18 20:22:12', NULL,
+        NULL);
+INSERT INTO sys_menu
+VALUES (110204, '角色详情', 1102, 3, 'sys:role:info', NULL, NULL, NULL, NULL, 4, 1, 0, 1, 1, '2023-06-18 20:22:12', NULL,
+        NULL);
+INSERT INTO sys_menu
+VALUES (110207, '设置角色权限', 1102, 3, 'sys:role:set-role-menus', NULL, NULL, NULL, NULL, 5, 1, 0, 1, 1,
+        '2023-06-18 20:22:12', NULL, NULL);
+INSERT INTO sys_menu
+VALUES (110301, '添加菜单', 1103, 3, 'sys:menu:add', NULL, NULL, NULL, NULL, 1, 1, 0, 1, 1, '2023-06-18 20:22:12', NULL,
+        NULL);
+INSERT INTO sys_menu
+VALUES (110302, '修改菜单', 1103, 3, 'sys:menu:update', NULL, NULL, NULL, NULL, 2, 1, 0, 1, 1, '2023-06-18 20:22:12', NULL,
+        NULL);
+INSERT INTO sys_menu
+VALUES (110303, '删除菜单', 1103, 3, 'sys:menu:delete', NULL, NULL, NULL, NULL, 3, 1, 0, 1, 1, '2023-06-18 20:22:12', NULL,
+        NULL);
+INSERT INTO sys_menu
+VALUES (110304, '菜单详情', 1103, 3, 'sys:menu:info', NULL, NULL, NULL, NULL, 4, 1, 0, 1, 1, '2023-06-18 20:22:12', NULL,
+        NULL);
+INSERT INTO sys_menu
+VALUES (11050101, '日志详情', 110501, 3, 'sys:log:info', NULL, NULL, NULL, NULL, 2, 0, 0, 1, 1, '2023-06-23 21:11:00', NULL,
+        '2023-06-23 21:24:05');
+INSERT INTO sys_menu
+VALUES (11050102, '日志列表', 110501, 3, 'sys:log:list', NULL, NULL, NULL, NULL, 3, 1, 0, 1, 1, '2023-06-23 21:12:04', NULL,
+        '2023-06-23 21:24:13');
+INSERT INTO sys_menu
+VALUES (488299583537157, '字典管理', 11, 2, 'dict', '/system/dict', 'system/dict/index', NULL, 'ele-notebook', 3, 1, 0, 1,
+        1, '2023-11-30 21:16:07', NULL, NULL);
+INSERT INTO sys_menu
+VALUES (489978240356357, '系统配置', 11, 2, 'sys:config:manager', '/system/config', 'system/config/index', NULL, 'ele-cpu',
+        4, 1, 0, 1, 1, '2023-12-05 15:06:35', NULL, '2023-12-05 16:18:59');
+INSERT INTO sys_menu
+VALUES (499911770910725, '系统工具', 0, 1, '', '/tool', '', '', 'ele-monitor', 98, 1, 0, 1, 1, '2024-01-02 16:46:14', 1,
+        '2024-01-21 20:15:22');
+INSERT INTO sys_menu
+VALUES (499912002056197, '代码生成', 499911770910725, 2, 'code', '/tool/code', 'tool/code/index', '', 'ele-document-copy',
+        1, 1, 0, 1, 1, '2024-01-02 16:47:11', 1, '2024-01-21 20:15:29');
+INSERT INTO sys_menu
+VALUES (504136134303749, '生成配置', 499911770910725, 2, 'generator:table:update', '/tool/code/config', 'tool/code/config',
+        NULL, 'ele-suitcase-line', 1, 0, 0, 1, 1, '2024-01-14 15:15:13', 1, '2024-01-14 15:15:12');
+INSERT INTO sys_menu
+VALUES (506680317689861, '预览代码', 499912002056197, 3, 'generator:preview-code', NULL, NULL, NULL, NULL, 2, 0, 0, 1, 1,
+        '2024-01-21 19:47:31', 1, '2024-01-21 19:47:31');
+INSERT INTO sys_menu
+VALUES (506686662549509, '编辑配置', 499912002056197, 3, 'generator:table:update', NULL, NULL, NULL, NULL, 3, 0, 0, 1, 1,
+        '2024-01-21 20:13:20', 1, '2024-01-21 20:13:20');
+INSERT INTO sys_menu
+VALUES (506686906273797, '同步表信息', 499912002056197, 3, 'generator:sync-code', NULL, NULL, NULL, NULL, 4, 0, 0, 1, 1,
+        '2024-01-21 20:14:20', 1, '2024-01-21 20:14:20');
+INSERT INTO sys_menu
+VALUES (506687030714373, '生成代码', 499912002056197, 3, 'generator:generator-code', NULL, NULL, NULL, NULL, 5, 0, 0, 1, 1,
+        '2024-01-21 20:14:50', 1, '2024-01-21 20:14:50');
+INSERT INTO sys_menu
+VALUES (506687093297157, '下载代码', 499912002056197, 3, 'generator:download-code', NULL, NULL, NULL, NULL, 6, 0, 0, 1, 1,
+        '2024-01-21 20:15:06', 1, '2024-01-21 20:15:05');
+
+
+-- ----------------------------
+-- Table structure for sys_role
+-- ----------------------------
+DROP TABLE IF EXISTS sys_role;
+CREATE TABLE sys_role
+(
+    id          bigint(0)   NOT NULL COMMENT '主键',
+    code        varchar(100)         DEFAULT NULL COMMENT '角色唯一编码',
+    name        varchar(32) NOT NULL COMMENT '角色名称',
+    is_system   tinyint(1)  NOT NULL DEFAULT 0 COMMENT '是否系统内置角色 1：是，0：否',
+    remark      varchar(200)         DEFAULT NULL COMMENT '备注',
+    create_id   bigint(0)            DEFAULT NULL COMMENT '创建人ID',
+    create_time datetime(0)          DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id   bigint(0)            DEFAULT NULL COMMENT '修改人ID',
+    update_time datetime(0)          DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE,
+    UNIQUE INDEX sys_role_name_uindex (name) USING BTREE,
+    UNIQUE INDEX sys_role_code_uindex (code) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '系统角色'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Records of sys_role
+-- ----------------------------
+INSERT INTO sys_role
+VALUES (1, 'admin', '管理员', 1, NULL, 1, '2023-02-15 13:22:21', NULL, NULL);
+INSERT INTO sys_role
+VALUES (497014598377477, 'test', '测试', 1, '测试角色，普通权限1', 1, '2023-12-25 12:17:37', 1, '2023-12-25 12:24:52');
+INSERT INTO sys_role
+VALUES (507811226501125, 'custom_role', '自定义角色', 0, '用户自定义角色', 1, '2024-01-25 00:29:12', NULL, '2024-01-25 00:29:12');
+
+-- ----------------------------
+-- Table structure for sys_role_menu
+-- ----------------------------
+DROP TABLE IF EXISTS sys_role_menu;
+CREATE TABLE sys_role_menu
+(
+    id          bigint(0) NOT NULL COMMENT '主键',
+    role_id     bigint(0)   DEFAULT NULL COMMENT '角色id',
+    menu_id     bigint(0)   DEFAULT NULL COMMENT '菜单id',
+    is_choice   tinyint(1)  DEFAULT NULL COMMENT '是否用户选中 0：否，1：是',
+    create_id   bigint(0)   DEFAULT NULL COMMENT '创建人ID',
+    create_time datetime(0) DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id   bigint(0)   DEFAULT NULL COMMENT '修改人ID',
+    update_time datetime(0) DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE,
+    INDEX sys_role_menu_menu_id_index (menu_id) USING BTREE,
+    INDEX sys_role_menu_role_id_index (role_id) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '角色菜单关系表'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Records of sys_role_menu
+-- ----------------------------
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (490996903432197, 490425003896837, 11, 1, 1, '2023-12-08 12:11:32', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (490996903436293, 490425003896837, 1101, 1, 1, '2023-12-08 12:11:32', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (491477241692165, 491472570187781, 110101, 1, 1, '2023-12-09 20:46:03', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (491477241700357, 491472570187781, 110102, 1, 1, '2023-12-09 20:46:03', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (491477241700358, 491472570187781, 11, 0, 1, '2023-12-09 20:46:03', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (491477241704453, 491472570187781, 1101, 0, 1, '2023-12-09 20:46:03', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (495707737645061, 495707261153285, 11, 1, 1, '2023-12-21 19:39:59', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (495707737657349, 495707261153285, 1101, 1, 1, '2023-12-21 19:39:59', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (495707737665541, 495707261153285, 110101, 1, 1, '2023-12-21 19:39:59', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (495707737669637, 495707261153285, 110102, 1, 1, '2023-12-21 19:39:59', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (497017157316613, 497014598377477, 1101, 1, 1, '2023-12-25 12:28:08', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (497017185705989, 497014598377477, 110101, 1, 1, '2023-12-25 12:28:08', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (497017185714181, 497014598377477, 110102, 1, 1, '2023-12-25 12:28:08', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (497017185730565, 497014598377477, 110103, 1, 1, '2023-12-25 12:28:08', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (497017185742853, 497014598377477, 110104, 1, 1, '2023-12-25 12:28:08', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (497017185845253, 497014598377477, 110106, 1, 1, '2023-12-25 12:28:08', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (497017185853445, 497014598377477, 11, 0, 1, '2023-12-25 12:28:08', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (500516359094277, 500516312236037, 110102, 1, 1, '2024-01-04 09:46:19', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (500516359110661, 500516312236037, 11, 0, 1, '2024-01-04 09:46:19', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (500516359114757, 500516312236037, 1101, 0, 1, '2024-01-04 09:46:19', null, null);
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (505545488814085, 497741963337733, 110201, 1, 1, '2024-01-18 14:49:54', null, '2024-01-18 14:49:53');
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (505545488818181, 497741963337733, 11, 0, 1, '2024-01-18 14:49:54', null, '2024-01-18 14:49:53');
+INSERT INTO sys_role_menu (id, role_id, menu_id, is_choice, create_id, create_time, update_id, update_time)
+VALUES (505545488818182, 497741963337733, 1102, 0, 1, '2024-01-18 14:49:54', null, '2024-01-18 14:49:53');
+
+
+-- ----------------------------
+-- Table structure for sys_user
+-- ----------------------------
+DROP TABLE IF EXISTS sys_user;
+CREATE TABLE sys_user
+(
+    id          bigint(0)   NOT NULL COMMENT '主键',
+    username    varchar(20) NOT NULL COMMENT '用户名',
+    nickname    varchar(20) NOT NULL COMMENT '昵称',
+    password    varchar(64) NOT NULL COMMENT '密码',
+    salt        varchar(32) NOT NULL COMMENT '盐值',
+    phone       varchar(20)          DEFAULT NULL COMMENT '手机号码',
+    email       varchar(255)         DEFAULT NULL COMMENT '电子邮件',
+    head        varchar(100)         DEFAULT NULL COMMENT '头像',
+    status      tinyint(1)  NOT NULL DEFAULT 1 COMMENT '状态，0：禁用，1：启用',
+    role_id     bigint(0)   NOT NULL COMMENT '角色ID',
+    is_admin    tinyint(1)  NOT NULL DEFAULT 0 COMMENT '是否是超管 0：否，1：是',
+    create_id   bigint(0)            DEFAULT NULL COMMENT '创建人ID',
+    create_time datetime(0)          DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id   bigint(0)            DEFAULT NULL COMMENT '修改人ID',
+    update_time datetime(0)          DEFAULT NULL COMMENT '修改时间',
+    PRIMARY KEY (id) USING BTREE,
+    UNIQUE INDEX sys_us (username) USING BTREE,
+    INDEX sys_user_status_index (status) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '系统用户'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Records of sys_user
+-- ----------------------------
+INSERT INTO sys_user
+VALUES (1, 'admin', '管理员', 'fa1a2b8314d2a19cb1881eeebc4747adcc31d0b3aa7fba8f45643f1ec8570883',
+        '970eb1c2f043455e9f70c35578f7e87c', '15892309104', '2422089289@qq.com',
+        'https://geekidea.oss-cn-chengdu.aliyuncs.com/boot/202312/20231212223125492564821676037.png', 1, 1, 1, 1,
+        '2023-12-12 22:33:06', NULL, '2023-12-25 12:04:31');
+INSERT INTO sys_user
+VALUES (497024442028037, 'test', '测试账号', '36d561f763fa5d40a886fa4511edb318b69529bd5cf1886b8034148a3bd1553d',
+        '9f6640f628a14d81ab94122c6d1ef41b', '15888888888', 'geekidea@qq.com', NULL, 1, 497014598377477, 0, 1,
+        '2023-12-25 12:57:40', 1, '2023-12-27 11:12:42');
+
+-- ----------------------------
+-- Table structure for user
+-- ----------------------------
+DROP TABLE IF EXISTS user;
+CREATE TABLE user
+(
+    id                 bigint(0)  NOT NULL COMMENT 'ID',
+    username           varchar(20)         DEFAULT NULL COMMENT '账号',
+    nickname           varchar(20)         DEFAULT NULL COMMENT '昵称',
+    password           varchar(64)         DEFAULT NULL COMMENT '密码',
+    salt               varchar(32)         DEFAULT NULL COMMENT '盐值',
+    openid             varchar(200)        DEFAULT NULL COMMENT '微信openid',
+    phone              varchar(11)         DEFAULT NULL COMMENT '手机号码',
+    head               varchar(200)        DEFAULT NULL COMMENT '头像',
+    introduction       varchar(200)        DEFAULT NULL COMMENT '个人简介',
+    user_role_id       bigint(0)           DEFAULT NULL COMMENT '用户角色ID',
+    status             tinyint(1) NOT NULL DEFAULT 1 COMMENT '状态 1：正常，0：禁用',
+    register_time      datetime(0)         DEFAULT NULL COMMENT '注册时间',
+    register_ip        varchar(20)         DEFAULT NULL COMMENT '注册IP',
+    register_ip_area   varchar(100)        DEFAULT NULL COMMENT '注册IP区域',
+    last_login_time    datetime(0)         DEFAULT NULL COMMENT '最后登录时间',
+    last_login_ip      varchar(20)         DEFAULT NULL COMMENT '最后登录IP',
+    last_login_ip_area varchar(100)        DEFAULT NULL COMMENT '最后一次登录IP区域',
+    remark             varchar(200)        DEFAULT NULL COMMENT '备注',
+    create_id          bigint(0)           DEFAULT NULL COMMENT '创建人ID',
+    create_time        datetime(0)         DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id          bigint(0)           DEFAULT NULL COMMENT '修改人ID',
+    update_time        datetime(0)         DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE,
+    UNIQUE INDEX user_openid_uindex (openid) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '用户信息'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Records of user
+-- ----------------------------
+INSERT INTO user
+VALUES (1, 'boot', 'boot用户11', '35d9d2ad09f0a7b0c2b3143bd60affb719766972aa720827c97240ea04d0f304',
+        '94132dcd55eb478bb63170a2fa510ea1', NULL, NULL, NULL, NULL, 1, 1, '2023-11-25 00:00:00', NULL, NULL,
+        '2024-02-12 16:29:45', '127.0.0.1', '内网IP', NULL, NULL, '2023-11-25 23:54:31', 1, '2024-01-12 23:07:01');
+INSERT INTO user
+VALUES (2, 'geekidea', 'geekidea用户', '35d9d2ad09f0a7b0c2b3143bd60affb719766972aa720827c97240ea04d0f304',
+        '94132dcd55eb478bb63170a2fa510ea1', NULL, NULL, NULL, NULL, 2, 1, '2023-11-25 00:00:00', NULL, NULL,
+        '2023-11-26 00:56:15', '127.0.0.1', '内网IP', NULL, NULL, '2023-11-25 23:54:31', NULL, NULL);
+
+-- ----------------------------
+-- Table structure for user_banner
+-- ----------------------------
+DROP TABLE IF EXISTS user_banner;
+CREATE TABLE user_banner
+(
+    id               bigint(0)    NOT NULL COMMENT 'ID',
+    type             tinyint(0)   NOT NULL COMMENT '轮播图类型 1：首页轮播图，2：广告轮播图',
+    name             varchar(32)  NOT NULL COMMENT '轮播图名称',
+    image_url        varchar(200) NOT NULL COMMENT '轮播图图片路径',
+    jump_type        tinyint(0)   NOT NULL COMMENT '跳转类型 1：不跳转，2：跳转到商品详情，3：跳转到广告详情，4：跳转到客服页面',
+    jump_business_id bigint(0)   DEFAULT NULL COMMENT '跳转到业务ID',
+    create_id        bigint(0)   DEFAULT NULL COMMENT '创建人ID',
+    create_time      datetime(0) DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id        bigint(0)   DEFAULT NULL COMMENT '修改人ID',
+    update_time      datetime(0) DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '用户轮播图'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Table structure for user_role
+-- ----------------------------
+DROP TABLE IF EXISTS user_role;
+CREATE TABLE user_role
+(
+    id          bigint(0)   NOT NULL COMMENT '主键',
+    code        varchar(100) DEFAULT NULL COMMENT '用户角色唯一编码',
+    name        varchar(32) NOT NULL COMMENT '用户角色名称',
+    remark      varchar(200) DEFAULT NULL COMMENT '备注',
+    create_id   bigint(0)    DEFAULT NULL COMMENT '创建人ID',
+    create_time datetime(0)  DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    update_id   bigint(0)    DEFAULT NULL COMMENT '修改人ID',
+    update_time datetime(0)  DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (id) USING BTREE,
+    UNIQUE INDEX user_role_name_uindex (name) USING BTREE,
+    UNIQUE INDEX user_role_code_uindex (code) USING BTREE
+) ENGINE = InnoDB
+  CHARACTER SET = utf8mb4
+  COLLATE = utf8mb4_general_ci COMMENT = '用户角色'
+  ROW_FORMAT = Dynamic;
+
+-- ----------------------------
+-- Records of user_role
+-- ----------------------------
+INSERT INTO user_role
+VALUES (1, 'normal', '普通用户', NULL, NULL, '2024-01-06 21:21:23', NULL, NULL);
+INSERT INTO user_role
+VALUES (2, 'vip', 'VIP用户', NULL, NULL, '2024-01-06 21:21:23', NULL, NULL);
+
diff --git a/distribution/.gitignore b/distribution/.gitignore
deleted file mode 100644
index ccdf702f..00000000
--- a/distribution/.gitignore
+++ /dev/null
@@ -1,36 +0,0 @@
-/target/
-/classes
-!.mvn/wrapper/maven-wrapper.jar
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-
-### NetBeans ###
-/nbproject/private/
-/build/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-
-.DS_Store
-
-*.log
-logs
-
-*.rdb
-
-
-
diff --git a/distribution/README.md b/distribution/README.md
deleted file mode 100644
index dd11a0a0..00000000
--- a/distribution/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# distribution 项目打包模块
\ No newline at end of file
diff --git a/distribution/admin/bin/restart.sh b/distribution/admin/bin/restart.sh
deleted file mode 100644
index e1106feb..00000000
--- a/distribution/admin/bin/restart.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#! /bin/shell
-
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 项目重启shell脚本
-# 先调用shutdown.sh停服
-# 然后调用startup.sh启动服务
-#
-# author: geekidea
-# date: 2020-3-28
-#======================================================================
-
-# 项目名称
-
-# 停服
-sh shutdown.sh
-
-# 启动服务
-sh startup.sh
\ No newline at end of file
diff --git a/distribution/admin/bin/shutdown.sh b/distribution/admin/bin/shutdown.sh
deleted file mode 100644
index 6cbb4d03..00000000
--- a/distribution/admin/bin/shutdown.sh
+++ /dev/null
@@ -1,40 +0,0 @@
-#! /bin/shell
-
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 项目停服shell脚本
-# 通过项目名称查找到PID
-# 然后kill -9 pid
-#
-# author: geekidea
-# date: 2020-3-28
-#======================================================================
-
-# 项目名称
-APPLICATION="${admin.artifact.name}"
-
-# 项目启动jar包名称
-APPLICATION_JAR="${APPLICATION}.jar"
-
-PID=$(ps -ef | grep ${APPLICATION_JAR} | grep -v grep | awk '{ print $2 }')
-if [ -z "$PID" ]
-then
-    echo ${APPLICATION} is already stopped
-else
-    echo kill  ${PID}
-    kill -9 ${PID}
-    echo ${APPLICATION} stopped successfully
-fi
\ No newline at end of file
diff --git a/distribution/admin/bin/startup.sh b/distribution/admin/bin/startup.sh
deleted file mode 100644
index f2674eb4..00000000
--- a/distribution/admin/bin/startup.sh
+++ /dev/null
@@ -1,124 +0,0 @@
-#! /bin/shell
-
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# Admin项目启动shell脚本
-# config目录: 配置文件目录
-# logs目录: 项目运行日志目录
-# logs/spring-boot-plus_startup.log: 记录启动日志
-# logs/back目录: 项目运行日志备份目录
-# nohup后台运行
-#
-# author: geekidea
-# date: 2020-3-28
-#======================================================================
-
-# 项目名称
-APPLICATION="${admin.artifact.name}"
-
-# 项目启动jar包名称
-APPLICATION_JAR="${APPLICATION}.jar"
-
-echo ${APPLICATION_JAR}
-
-# bin目录绝对路径
-BIN_PATH=$(cd `dirname $0`; pwd)
-
-
-# 进入bin目录
-cd `dirname $0`
-# 返回到上一级项目根目录路径
-cd ..
-# 打印项目根目录绝对路径
-# `pwd` 执行系统命令并获得结果
-BASE_PATH=`pwd`
-
-# 外部配置文件绝对目录,如果是目录需要/结尾，也可以直接指定文件
-# 如果指定的是目录,spring则会读取目录中的所有配置文件
-CONFIG_DIR=${BASE_PATH}"/config/"
-
-# 项目日志输出绝对路径
-LOG_DIR=${BASE_PATH}"/logs"
-LOG_FILE="${APPLICATION}.log"
-LOG_PATH="${LOG_DIR}/${LOG_FILE}"
-# 日志备份目录
-LOG_BACK_DIR="${LOG_DIR}/back/"
-
-# 项目启动日志输出绝对路径
-LOG_STARTUP_PATH="${LOG_DIR}/${APPLICATION}_startup.log"
-
-# 当前时间
-NOW=$(date --date='0 days ago' "+%Y-%m-%d-%H-%M-%S")
-NOW_PRETTY=$(date --date='0 days ago' "+%Y-%m-%d %H:%M:%S")
-
-# 启动日志
-STARTUP_LOG="================================================ ${NOW_PRETTY} ================================================\n"
-
-# 如果logs文件夹不存在,则创建文件夹
-if [ ! -d "${LOG_DIR}" ]; then
-  mkdir "${LOG_DIR}"
-fi
-
-# 如果logs/back文件夹不存在,则创建文件夹
-if [ ! -d "${LOG_BACK_DIR}" ]; then
-  mkdir "${LOG_BACK_DIR}"
-fi
-
-# 如果项目运行日志存在,则重命名备份
-if [ -f "${LOG_PATH}" ]; then
-	mv ${LOG_PATH} "${LOG_BACK_DIR}/${APPLICATION}_back_${NOW}.log"
-fi
-
-# 创建新的项目运行日志
-echo "" > ${LOG_PATH}
-
-# 如果项目启动日志不存在,则创建,否则追加
-echo ${STARTUP_LOG} >> ${LOG_STARTUP_PATH}
-
-#=======================================================
-# 将命令启动相关日志追加到日志文件
-#=======================================================
-
-# 输出项目名称
-STARTUP_LOG="${STARTUP_LOG}application name: ${APPLICATION}\n"
-# 输出jar包名称
-STARTUP_LOG="${STARTUP_LOG}application jar name: ${APPLICATION_JAR}\n"
-# 输出项目bin路径
-STARTUP_LOG="${STARTUP_LOG}application bin  path: ${BIN_PATH}\n"
-# 输出项目根目录
-STARTUP_LOG="${STARTUP_LOG}application root path: ${BASE_PATH}\n"
-# 打印日志路径
-STARTUP_LOG="${STARTUP_LOG}application log  path: ${LOG_PATH}\n"
-
-# 打印启动命令
-STARTUP_LOG="${STARTUP_LOG}application background startup command: nohup java -jar ${BASE_PATH}/lib/${APPLICATION_JAR} --spring.config.location=${CONFIG_DIR} > ${LOG_PATH} 2>&1 &\n"
-
-#======================================================================
-# 执行启动命令：后台启动项目,并将日志输出到项目根目录下的logs文件夹下
-#======================================================================
-nohup java -jar ${BASE_PATH}/lib/${APPLICATION_JAR} --spring.config.location=${CONFIG_DIR} > ${LOG_PATH} 2>&1 &
-
-# 进程ID
-PID=$(ps -ef | grep ${APPLICATION_JAR} | grep -v grep | awk '{ print $2 }')
-STARTUP_LOG="${STARTUP_LOG}application pid: ${PID}\n"
-
-# 启动日志追加到启动日志文件中
-echo -e ${STARTUP_LOG} >> ${LOG_STARTUP_PATH}
-# 打印启动日志
-echo -e ${STARTUP_LOG}
-
-# 打印项目日志
-tail -f ${LOG_PATH}
\ No newline at end of file
diff --git a/distribution/bin/restart.sh b/distribution/bin/restart.sh
deleted file mode 100644
index 5f823bc8..00000000
--- a/distribution/bin/restart.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#! /bin/shell
-
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 项目重启shell脚本
-# 先调用shutdown.sh停服
-# 然后调用startup.sh启动服务
-#
-# author: geekidea
-# date: 2018-12-2
-#======================================================================
-
-# 项目名称
-
-# 停服
-sh shutdown.sh
-
-# 启动服务
-sh startup.sh
\ No newline at end of file
diff --git a/distribution/bin/shutdown.sh b/distribution/bin/shutdown.sh
deleted file mode 100644
index c7ed2d0e..00000000
--- a/distribution/bin/shutdown.sh
+++ /dev/null
@@ -1,40 +0,0 @@
-#! /bin/shell
-
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 项目停服shell脚本
-# 通过项目名称查找到PID
-# 然后kill -9 pid
-#
-# author: geekidea
-# date: 2018-12-2
-#======================================================================
-
-# 项目名称
-APPLICATION="${boot.artifact.name}"
-
-# 项目启动jar包名称
-APPLICATION_JAR="${APPLICATION}.jar"
-
-PID=$(ps -ef | grep ${APPLICATION_JAR} | grep -v grep | awk '{ print $2 }')
-if [ -z "$PID" ]
-then
-    echo ${APPLICATION} is already stopped
-else
-    echo kill  ${PID}
-    kill -9 ${PID}
-    echo ${APPLICATION} stopped successfully
-fi
\ No newline at end of file
diff --git a/distribution/bin/startup.sh b/distribution/bin/startup.sh
deleted file mode 100644
index df4aa1e6..00000000
--- a/distribution/bin/startup.sh
+++ /dev/null
@@ -1,142 +0,0 @@
-#! /bin/shell
-
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 项目启动shell脚本
-# config目录: 配置文件目录
-# logs目录: 项目运行日志目录
-# logs/spring-boot-plus_startup.log: 记录启动日志
-# logs/back目录: 项目运行日志备份目录
-# nohup后台运行
-#
-# author: geekidea
-# date: 2018-12-2
-#======================================================================
-
-# 项目名称
-APPLICATION="${boot.artifact.name}"
-
-# 项目启动jar包名称
-APPLICATION_JAR="${APPLICATION}.jar"
-
-echo ${APPLICATION_JAR}
-
-# bin目录绝对路径
-BIN_PATH=$(cd `dirname $0`; pwd)
-
-
-# 进入bin目录
-cd `dirname $0`
-# 返回到上一级项目根目录路径
-cd ..
-# 打印项目根目录绝对路径
-# `pwd` 执行系统命令并获得结果
-BASE_PATH=`pwd`
-
-# 外部配置文件绝对目录,如果是目录需要/结尾，也可以直接指定文件
-# 如果指定的是目录,spring则会读取目录中的所有配置文件
-CONFIG_DIR=${BASE_PATH}"/config/"
-
-# 项目日志输出绝对路径
-LOG_DIR=${BASE_PATH}"/logs"
-LOG_FILE="${APPLICATION}.log"
-LOG_PATH="${LOG_DIR}/${LOG_FILE}"
-# 日志备份目录
-LOG_BACK_DIR="${LOG_DIR}/back/"
-
-# 项目启动日志输出绝对路径
-LOG_STARTUP_PATH="${LOG_DIR}/${APPLICATION}_startup.log"
-
-# 当前时间
-NOW=$(date --date='0 days ago' "+%Y-%m-%d-%H-%M-%S")
-NOW_PRETTY=$(date --date='0 days ago' "+%Y-%m-%d %H:%M:%S")
-
-# 启动日志
-STARTUP_LOG="================================================ ${NOW_PRETTY} ================================================\n"
-
-# 如果logs文件夹不存在,则创建文件夹
-if [ ! -d "${LOG_DIR}" ]; then
-  mkdir "${LOG_DIR}"
-fi
-
-# 如果logs/back文件夹不存在,则创建文件夹
-if [ ! -d "${LOG_BACK_DIR}" ]; then
-  mkdir "${LOG_BACK_DIR}"
-fi
-
-# 如果项目运行日志存在,则重命名备份
-if [ -f "${LOG_PATH}" ]; then
-	mv ${LOG_PATH} "${LOG_BACK_DIR}/${APPLICATION}_back_${NOW}.log"
-fi
-
-# 创建新的项目运行日志
-echo "" > ${LOG_PATH}
-
-# 如果项目启动日志不存在,则创建,否则追加
-echo ${STARTUP_LOG} >> ${LOG_STARTUP_PATH}
-
-#==========================================================================================
-# JVM Configuration
-# -Xmx1g:设置JVM最大可用内存为1G。
-# -Xms1g:设置JVM初始内存1g。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存
-# -Xmn512m:设置年轻代大小为512m。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。
-#          持久代一般固定大小为64m,所以增大年轻代,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-# -XX:MetaspaceSize=64m:存储class的内存大小,该值越大触发Metaspace GC的时机就越晚
-# -XX:MaxMetaspaceSize=256m:限制Metaspace增长的上限，防止因为某些情况导致Metaspace无限的使用本地内存，影响到其他程序
-# -XX:-OmitStackTraceInFastThrow:解决重复异常不打印堆栈信息问题
-#==========================================================================================
-JAVA_OPT="-server -Xms1g -Xmx1g -Xmn512m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=256m"
-JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"
-
-#=======================================================
-# 将命令启动相关日志追加到日志文件
-#=======================================================
-
-# 输出项目名称
-STARTUP_LOG="${STARTUP_LOG}application name: ${APPLICATION}\n"
-# 输出jar包名称
-STARTUP_LOG="${STARTUP_LOG}application jar name: ${APPLICATION_JAR}\n"
-# 输出项目bin路径
-STARTUP_LOG="${STARTUP_LOG}application bin  path: ${BIN_PATH}\n"
-# 输出项目根目录
-STARTUP_LOG="${STARTUP_LOG}application root path: ${BASE_PATH}\n"
-# 打印日志路径
-STARTUP_LOG="${STARTUP_LOG}application log  path: ${LOG_PATH}\n"
-# 打印JVM配置
-STARTUP_LOG="${STARTUP_LOG}application JAVA_OPT : ${JAVA_OPT}\n"
-
-
-# 打印启动命令
-STARTUP_LOG="${STARTUP_LOG}application background startup command: nohup java ${JAVA_OPT} -jar ${BASE_PATH}/lib/${APPLICATION_JAR} --spring.config.location=${CONFIG_DIR} --logging.config=${CONFIG_DIR}logback.xml > ${LOG_PATH} 2>&1 &\n"
-
-
-#======================================================================
-# 执行启动命令：后台启动项目,并将日志输出到项目根目录下的logs文件夹下
-#======================================================================
-nohup java ${JAVA_OPT} -jar ${BASE_PATH}/lib/${APPLICATION_JAR} --spring.config.location=${CONFIG_DIR} --logging.config=${CONFIG_DIR}logback.xml > ${LOG_PATH} 2>&1 &
-
-
-# 进程ID
-PID=$(ps -ef | grep ${APPLICATION_JAR} | grep -v grep | awk '{ print $2 }')
-STARTUP_LOG="${STARTUP_LOG}application pid: ${PID}\n"
-
-# 启动日志追加到启动日志文件中
-echo -e ${STARTUP_LOG} >> ${LOG_STARTUP_PATH}
-# 打印启动日志
-echo -e ${STARTUP_LOG}
-
-# 打印项目日志
-tail -f ${LOG_PATH}
\ No newline at end of file
diff --git a/distribution/pom.xml b/distribution/pom.xml
deleted file mode 100644
index 85b89ce7..00000000
--- a/distribution/pom.xml
+++ /dev/null
@@ -1,111 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>io.geekidea.springbootplus</groupId>
-        <artifactId>parent</artifactId>
-        <version>2.0</version>
-    </parent>
-
-    <artifactId>distribution</artifactId>
-    <name>distribution</name>
-    <description>项目打包模块</description>
-
-    <profiles>
-        <profile>
-            <id>release</id>
-            <dependencies>
-                <dependency>
-                    <groupId>io.geekidea.springbootplus</groupId>
-                    <artifactId>bootstrap</artifactId>
-                </dependency>
-            </dependencies>
-
-            <build>
-                <resources>
-                    <resource>
-                        <directory>bin</directory>
-                        <filtering>true</filtering>
-                        <targetPath>bin</targetPath>
-                    </resource>
-                </resources>
-                <plugins>
-                    <plugin>
-                        <artifactId>maven-assembly-plugin</artifactId>
-                        <version>${maven-assembly-plugin.version}</version>
-                        <configuration>
-                            <finalName>${assembly.name}</finalName>
-                            <descriptors>
-                                <descriptor>release.xml</descriptor>
-                            </descriptors>
-                            <appendAssemblyId>false</appendAssemblyId>
-                        </configuration>
-                        <executions>
-                            <execution>
-                                <phase>package</phase>
-                                <goals>
-                                    <goal>single</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-
-        <profile>
-            <id>release-admin</id>
-            <build>
-                <resources>
-                    <resource>
-                        <directory>admin</directory>
-                        <filtering>true</filtering>
-                        <targetPath>admin</targetPath>
-                    </resource>
-                </resources>
-                <plugins>
-                    <plugin>
-                        <artifactId>maven-assembly-plugin</artifactId>
-                        <version>${maven-assembly-plugin.version}</version>
-                        <configuration>
-                            <finalName>${assembly.name}-admin</finalName>
-                            <descriptors>
-                                <descriptor>release-admin.xml</descriptor>
-                            </descriptors>
-                            <appendAssemblyId>false</appendAssemblyId>
-                        </configuration>
-                        <executions>
-                            <execution>
-                                <id>make-assembly</id>
-                                <phase>package</phase>
-                                <goals>
-                                    <goal>single</goal>
-                                </goals>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
-
-</project>
diff --git a/distribution/release-admin.xml b/distribution/release-admin.xml
deleted file mode 100644
index 33c3d980..00000000
--- a/distribution/release-admin.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<assembly>
-    <id>admin</id>
-    <includeBaseDirectory>true</includeBaseDirectory>
-
-    <formats>
-        <format>dir</format>
-        <format>tar.gz</format>
-        <format>zip</format>
-    </formats>
-
-    <fileSets>
-        <!--
-            0755->即用户具有读/写/执行权限，组用户和其它用户具有读写权限；
-            0644->即用户具有读写权限，组用户和其它用户具有只读权限；
-        -->
-        <fileSet>
-            <directory>target/classes/admin/bin</directory>
-            <outputDirectory>bin</outputDirectory>
-            <fileMode>0755</fileMode>
-        </fileSet>
-
-        <fileSet>
-            <directory>admin/config</directory>
-            <outputDirectory>config</outputDirectory>
-            <fileMode>0755</fileMode>
-        </fileSet>
-
-        <fileSet>
-            <directory>admin/logs</directory>
-            <outputDirectory>logs</outputDirectory>
-            <fileMode>0755</fileMode>
-        </fileSet>
-
-        <fileSet>
-            <directory>../admin/target/classes</directory>
-            <includes>
-                <include>application.yml</include>
-            </includes>
-            <outputDirectory>config</outputDirectory>
-            <fileMode>0755</fileMode>
-        </fileSet>
-
-        <fileSet>
-            <directory>../admin/target</directory>
-            <includes>
-                <include>*.jar</include>
-            </includes>
-            <outputDirectory>lib</outputDirectory>
-            <fileMode>0755</fileMode>
-        </fileSet>
-
-        <fileSet>
-            <directory>../</directory>
-            <includes>
-                <include>LICENSE</include>
-                <include>README.md</include>
-            </includes>
-        </fileSet>
-    </fileSets>
-
-</assembly>
\ No newline at end of file
diff --git a/distribution/release.xml b/distribution/release.xml
deleted file mode 100644
index b46646fa..00000000
--- a/distribution/release.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<assembly>
-    <id>server</id>
-    <includeBaseDirectory>true</includeBaseDirectory>
-
-    <formats>
-        <format>dir</format>
-        <format>tar.gz</format>
-        <format>zip</format>
-    </formats>
-
-    <fileSets>
-        <!--
-            0755->即用户具有读/写/执行权限，组用户和其它用户具有读写权限；
-            0644->即用户具有读写权限，组用户和其它用户具有只读权限；
-        -->
-        <fileSet>
-            <directory>target/classes/bin</directory>
-            <outputDirectory>bin</outputDirectory>
-            <fileMode>0755</fileMode>
-        </fileSet>
-
-        <fileSet>
-            <includes>
-                <include>config/**</include>
-            </includes>
-        </fileSet>
-
-        <fileSet>
-            <includes>
-                <include>logs/**</include>
-            </includes>
-        </fileSet>
-
-        <fileSet>
-            <directory>../config/target/classes/config</directory>
-            <outputDirectory>config</outputDirectory>
-            <fileMode>0755</fileMode>
-        </fileSet>
-
-        <fileSet>
-            <directory>../</directory>
-            <includes>
-                <include>LICENSE</include>
-                <include>README.md</include>
-            </includes>
-        </fileSet>
-    </fileSets>
-
-    <moduleSets>
-        <moduleSet>
-            <useAllReactorProjects>true</useAllReactorProjects>
-            <includes>
-                <include>io.geekidea.springbootplus:bootstrap</include>
-            </includes>
-            <binaries>
-                <!-- 将依赖的jar包抽取到lib目录中 -->
-                <outputDirectory>lib/</outputDirectory>
-                <unpack>false</unpack>
-                <dependencySets>
-                    <dependencySet>
-                        <outputDirectory>lib/</outputDirectory>
-                    </dependencySet>
-                </dependencySets>
-            </binaries>
-        </moduleSet>
-    </moduleSets>
-
-</assembly>
\ No newline at end of file
diff --git a/docs/TODO.txt b/docs/TODO.txt
new file mode 100644
index 00000000..e180cbfc
--- /dev/null
+++ b/docs/TODO.txt
@@ -0,0 +1,10 @@
+前端代码弹窗关闭，重置表单默认值优化，比如默认选择启用按钮问题等
+前端系统配置调整列表列的宽度
+前端字典配置，默认选择第一个字典类型
+后台字典接口提供按字典类型不分页查询的列表接口
+拦截器handler instanceof HandlerMethod 判断问题，目前发现@RestController正常，@Controller时不会进入
+前端隐藏菜单问题，设置为不显示，还是显示出来了
+Hazelcast缓存调研
+抛出自定义状态码设计优化
+sys_config代码示例演示
+表注释去除前后空格和换行符
diff --git a/docs/bin/deploy.sh b/docs/bin/deploy.sh
index ae9da7cc..4fb24c9d 100644
--- a/docs/bin/deploy.sh
+++ b/docs/bin/deploy.sh
@@ -1,48 +1,37 @@
 #! /bin/shell
 
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 1. 下载或更新spring-boot-plus版本库
-# 2. maven打包
-# 3. 停服
-# 4. 备份
-# 5. 运行spring-boot-plus
-# author: geekidea
-# date: 2020-3-25
-#======================================================================
-
+# 当前时间
 NOW=$(date --date='0 days ago' "+%Y-%m-%d-%H-%M-%S")
 echo "${NOW}"
+# 变量配置
+# 代码更新结果
 PULL_RESULT=""
 IS_UPDATE=0
-# 项目名称
+# 服务路径，注意需在服务器上创建如下服务目录，然后将deploy.sh、start.sh、shutdown.sh放到其目录下
+SERVER_PATH="/home/spring-boot-plus-server"
+# GIT项目名称
 PROJECT_NAME="spring-boot-plus"
 # 版本库路径
-REPO_URL="https://gitee.com/geekidea/spring-boot-plus.git"
-# 打包文件名称
-ASSEMBLY_NAME="spring-boot-plus-server-2.0.tar.gz"
-# 服务目录名称
-SERVER_DIR="spring-boot-plus-server-2.0"
+REPO_URL="git@gitee.com:geekidea/spring-boot-plus.git"
+# jar包名称
+JAR_NAME="spring-boot-plus.jar"
+# 日志文件名称
+LOG_NAME="nohup.log"
+# 备份jar目录
+BACK_JAR_DIR="back-jar"
+# 备份jar目录
+BACK_LOG_DIR="back-log"
 # 发布的GIT分支名称
-DEPLOY_BRANCH=dev
+DEPLOY_BRANCH=master
 # 发布的Maven Profile
 ACTIVE_PROFILE=test
 
-# 1. 下载或更新spring-boot-plus版本库
-# 先判断当前目录下是否有spring-boot-plus目录
+# 进入到服务目录
+cd ${SERVER_PATH}
+pwd
+
+# 1. 下载或更新版本库
+# 先判断当前目录下是否有源代码目录
 # 如果有，则执行git pull
 # 如果没有，则clone
 if [ ! -d "${PROJECT_NAME}" ]; then
@@ -67,48 +56,51 @@ git checkout ${DEPLOY_BRANCH}
 git branch
 
 # 2. maven打包
-mvn clean install -P release,${ACTIVE_PROFILE}
-mvn clean package -P release,${ACTIVE_PROFILE}
+mvn clean install -P${ACTIVE_PROFILE}
 
 pwd
 # 判断是否生成成功
-if [ ! -f "distribution/target/${ASSEMBLY_NAME}" ]; then
+if [ ! -f "target/${JAR_NAME}" ]; then
   echo "maven build fail"
   exit
 fi
+echo "maven build success"
 
 # 3. 停服
-cd ..
+cd ${SERVER_PATH}
 pwd
 
-if [ -d "${SERVER_DIR}" ]; then
-  sh ${SERVER_DIR}/bin/shutdown.sh
+sh shutdown.sh
+
+# 4. 复制jar和日志文件到back目录下
+if [ ! -d "${BACK_JAR_DIR}" ]; then
+  mkdir ${BACK_JAR_DIR}
+fi
+if [ ! -d "${BACK_LOG_DIR}" ]; then
+  mkdir ${BACK_LOG_DIR}
 fi
 
-# 4. 复制spring-boot-plus-server-assembly.tar.gz到项目同级目录下
-# 备份之前的server
-if [ ! -d "${SERVER_DIR}-back" ]; then
-  mkdir ${SERVER_DIR}-back
+# 备份jar包
+if [ -f "${JAR_NAME}" ]; then
+  echo "back ${JAR_NAME}..."
+  mv ${JAR_NAME} ${BACK_JAR_DIR}/${JAR_NAME}"-${NOW}"
+  echo "back ${JAR_NAME} success"
 fi
 
-if [[ -d "${SERVER_DIR}" ]]
-then
-	echo "back ${SERVER_DIR}..."
-  mv ${SERVER_DIR} ${SERVER_DIR}-back/${SERVER_DIR}-back-"${NOW}"
-  echo "back success"
+# 备份日志
+if [ -f "${LOG_NAME}" ]; then
+  echo "back ${LOG_NAME}..."
+  mv ${LOG_NAME} ${BACK_LOG_DIR}/${LOG_NAME}"-${NOW}"
+  echo "back ${LOG_NAME} success"
 fi
 
-echo "copy ${ASSEMBLY_NAME}..."
+# 5. 复制打包后的jar到运行目录
+echo "copy ${SERVER_PATH}/${PROJECT_NAME}/target/${JAR_NAME}..."
 # 复制到项目同级目录，如果有，则覆盖
-cp -r -f ${PROJECT_NAME}/distribution/target/${ASSEMBLY_NAME} ${ASSEMBLY_NAME}
-echo "copy success"
-
-pwd
-# 5. 运行spring-boot-plus
-tar -zxvf ${ASSEMBLY_NAME}
-echo "tar.gz decompression success"
+cp -r -f ${PROJECT_NAME}/target/${JAR_NAME} .
+echo "copy ${JAR_NAME} success"
 
 pwd
-sh ${SERVER_DIR}/bin/shutdown.sh
-sh ${SERVER_DIR}/bin/startup.sh
+# 6. 运行jar
+sh start.sh
 
diff --git a/docs/bin/install/aliyun-mirrors.sh b/docs/bin/install/aliyun-mirrors.sh
index c4e1fc88..2766c17e 100644
--- a/docs/bin/install/aliyun-mirrors.sh
+++ b/docs/bin/install/aliyun-mirrors.sh
@@ -1,26 +1,5 @@
 #! /bin/shell
 
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 使用阿里云镜像
-#
-# author: geekidea
-# date: 2020-3-25
-#======================================================================
-
 yum clean all
 yum update
 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
diff --git a/docs/bin/install/download-install-all.sh b/docs/bin/install/download-install-all.sh
deleted file mode 100644
index 604e4a01..00000000
--- a/docs/bin/install/download-install-all.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#! /bin/shell
-
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 先下载按照脚本，多次执行该脚本，会覆盖以下下载的文件
-# 快速安装jdk/git/maven/redis/mysql
-#
-# author: geekidea
-# date: 2019-8-29
-#======================================================================
-
-# 下载脚本
-wget -O install-all.sh https://raw.githubusercontent.com/geekidea/spring-boot-plus/master/docs/bin/install/install-all.sh
-wget -O install-jdk.sh  https://raw.githubusercontent.com/geekidea/spring-boot-plus/master/docs/bin/install/install-jdk.sh
-wget -O install-git.sh  https://raw.githubusercontent.com/geekidea/spring-boot-plus/master/docs/bin/install/install-git.sh
-wget -O install-maven.sh  https://raw.githubusercontent.com/geekidea/spring-boot-plus/master/docs/bin/install/install-maven.sh
-wget -O install-redis.sh  https://raw.githubusercontent.com/geekidea/spring-boot-plus/master/docs/bin/install/install-redis.sh
-wget -O install-mysql.sh  https://raw.githubusercontent.com/geekidea/spring-boot-plus/master/docs/bin/install/install-mysql.sh
-wget -O mysql_spring_boot_plus.sql https://raw.githubusercontent.com/geekidea/spring-boot-plus/master/docs/db/mysql_spring_boot_plus.sql
-
-# 执行安装所有
-sh install-all.sh
\ No newline at end of file
diff --git a/docs/bin/install/install-all.sh b/docs/bin/install/install-all.sh
deleted file mode 100644
index a9a80d12..00000000
--- a/docs/bin/install/install-all.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#! /bin/shell
-
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 快速安装jdk/git/maven/redis/mysql
-#
-# author: geekidea
-# date: 2019-8-29
-#======================================================================
-
-sh install-jdk.sh
-sh install-git.sh
-sh install-maven.sh
-sh install-redis.sh
-sh install-mysql.sh
\ No newline at end of file
diff --git a/docs/bin/install/install-git.sh b/docs/bin/install/install-git.sh
index 8750f34a..4f97d62e 100644
--- a/docs/bin/install/install-git.sh
+++ b/docs/bin/install/install-git.sh
@@ -1,28 +1,5 @@
 #! /bin/shell
 
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 快速安装GIT
-# CentOS7 中已成功验证
-# 使用yum方式安装
-#
-# author: sy
-# date: 2019-8-29
-#======================================================================
-
 hasGit(){
     GIT_VERSION=$(git --version)
     echo "${GIT_VERSION}"
diff --git a/docs/bin/install/install-jdk.sh b/docs/bin/install/install-jdk.sh
index 8232333a..d7181d5d 100644
--- a/docs/bin/install/install-jdk.sh
+++ b/docs/bin/install/install-jdk.sh
@@ -1,29 +1,5 @@
 #! /bin/shell
 
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 快速安装JDK
-# CentOS7 中已成功验证
-# 使用yum方式安装
-#
-# author: sy
-# date: 2019-8-29
-#======================================================================
-
-
 hasJdk(){
     RESULT=$(pgrep java)
     if [[ ! $RESULT ]]
diff --git a/docs/bin/install/install-maven.sh b/docs/bin/install/install-maven.sh
deleted file mode 100644
index fb7d4500..00000000
--- a/docs/bin/install/install-maven.sh
+++ /dev/null
@@ -1,58 +0,0 @@
-#! /bin/shell
-
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 快速安装maven shell脚本
-#
-# author: chenkun
-# date: 2019-08-30
-#======================================================================
-
-hasMaven(){
-    MAVEN_VERSION=$(mvn -version)
-    echo "${MAVEN_VERSION}"
-    if [[ ! $MAVEN_VERSION ]]
-    then
-        return 0;
-    fi
-    return 1;
-}
-
-hasMaven
-
-if [ $? != 1 ]
-then
-    echo "Not Found maven"
-    echo "Installing maven..."
-
-    # 下载 maven aliyun mirror settings.xml
-    wget -O settings.xml https://raw.githubusercontent.com/geekidea/spring-boot-plus/master/docs/config/settings.xml
-
-    yum install -y maven
-
-    hasMaven
-    if [ $? == 1 ]
-    then
-      echo "Config Aliyun Maven Mirror..."
-      rm -rf /etc/maven/settings.xml
-      cp settings.xml /etc/maven/
-      mvn -version
-    else
-      echo "Install maven Fail"
-    fi
-fi
-
-echo ""
diff --git a/docs/bin/install/install-maven.txt b/docs/bin/install/install-maven.txt
new file mode 100644
index 00000000..1cf4948d
--- /dev/null
+++ b/docs/bin/install/install-maven.txt
@@ -0,0 +1,45 @@
+# 解决wget下载https时错误问题，更新证书
+yum install -y ca-certificates
+
+# 进入到目录，maven下载目录
+cd /home/tools
+
+# 下载maven3.6.2版本，下载太慢可本地下载后上传至服务器目录
+wget https://archive.apache.org/dist/maven/maven-3/3.6.2/binaries/apache-maven-3.6.2-bin.tar.gz
+
+# 解压
+tar -zxvf apache-maven-3.6.2-bin.tar.gz
+
+# 修改环境变量
+vi /etc/profile
+
+# 将如下配置添加到文件末尾出
+export MAVEN_HOME=/home/tools/apache-maven-3.6.2
+export PATH=$PATH:$MAVEN_HOME/bin
+
+# 刷新环境变量
+source /etc/profile
+
+# 查看maven版本
+mvn -v
+Apache Maven 3.6.2 (40f52333136460af0dc0d7232c0dc0bcf0d9e117; 2019-08-27T23:06:16+08:00)
+Maven home: /home/tools/apache-maven-3.6.2
+Java version: 1.8.0_372, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.372.b07-1.el7_9.x86_64/jre
+Default locale: en_US, platform encoding: UTF-8
+OS name: "linux", version: "3.10.0-862.el7.x86_64", arch: "amd64", family: "unix"
+
+# 设置settings.xml
+vim /home/tools/apache-maven-3.6.2/conf/settings.xml
+
+# 修改为aliyun maven镜像，在mirrors标签中添加如下配置
+
+<mirror>
+    <id>aliyun-maven</id>
+    <mirrorOf>central</mirrorOf>
+    <name>aliyun maven</name>
+    <url>http://maven.aliyun.com/repository/public</url>
+</mirror>
+
+# 初始化下载maven必要依赖
+mvn help:system
+
diff --git a/docs/bin/install/install-mysql.sh b/docs/bin/install/install-mysql.sh
index c454eafb..b99c2d36 100644
--- a/docs/bin/install/install-mysql.sh
+++ b/docs/bin/install/install-mysql.sh
@@ -1,36 +1,10 @@
 #! /bin/shell
 
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 快速安装MySQL
-# CentOS7 中已成功验证
-# 使用yum+rpm方式安装
-#
-# author: geekidea
-# date: 2019-8-29
-#======================================================================
-
-# 下载mysql rpm
-wget https://repo.mysql.com//mysql80-community-release-el7-3.noarch.rpm
-
-# 安装rpm
-rpm -Uvh mysql80-community-release-el7-3.noarch.rpm
-
-# yum 安装mysql服务
-yum install -y mysql-community-server
+# 更改mysql源，设置为mysql
+yum install -y https://repo.mysql.com//mysql80-community-release-el7-7.noarch.rpm
+
+# 安装mysql
+yum install -y mysql-server
 
 # 启动mysql服务
 systemctl start mysqld.service
@@ -38,6 +12,7 @@ systemctl start mysqld.service
 # 查看mysql服务状态
 systemctl status mysqld.service
 
+
 # 查看安装的mysql密码
 grep 'temporary password' /var/log/mysqld.log
 
@@ -48,6 +23,7 @@ echo "${PWD}"
 # 登录
 mysql -uroot -p${PWD}
 
+# 以下操作需要在mysql命令中执行，密码需自行修改！！！
 # 进入到mysql命令行时，修改密码
 # 修改密码
 # ALTER USER 'root'@'localhost' IDENTIFIED BY 'Springbootplus666!';
@@ -59,3 +35,16 @@ mysql -uroot -p${PWD}
 # 导入spring-boot-plus数据库脚本
 # use mysql;
 # source /root/mysql_spring_boot_plus.sql;
+
+# 修改密码
+# ALTER USER USER() IDENTIFIED BY 'Springbootplus666!';
+# FLUSH PRIVILEGES;
+# 退出重新登录mysql
+
+# 设置mysql外部访问
+# use mysql;
+# select host,user from user;
+# update user set host='%' where user ='root';
+# FLUSH PRIVILEGES;
+# GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'WITH GRANT OPTION;
+# FLUSH PRIVILEGES;
diff --git a/docs/bin/install/install-redis.sh b/docs/bin/install/install-redis.sh
index f85c49e7..b6f624db 100644
--- a/docs/bin/install/install-redis.sh
+++ b/docs/bin/install/install-redis.sh
@@ -1,26 +1,5 @@
 #! /bin/shell
 
-# Copyright 2019-2029 geekidea(https://github.com/geekidea)
-#
-# 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.
-
-#======================================================================
-# 快速安装redis shell脚本
-#
-# author: chenkun
-# date: 2019-08-30
-#======================================================================
-
 hasRedis(){
     RESULT=$(redis-server -v)
     echo "${RESULT}"
diff --git a/docs/bin/shutdown.sh b/docs/bin/shutdown.sh
new file mode 100644
index 00000000..2768fb02
--- /dev/null
+++ b/docs/bin/shutdown.sh
@@ -0,0 +1,13 @@
+#! /bin/shell
+
+# 停服
+APPLICATION="spring-boot-plus.jar"
+PID=$(ps -ef | grep ${APPLICATION} | grep -v grep | awk '{ print $2 }')
+if [ -z "$PID" ]
+then
+    echo ${APPLICATION} is already stopped
+else
+    echo kill  ${PID}
+    kill -9 ${PID}
+    echo ${APPLICATION} stopped successfully
+fi
\ No newline at end of file
diff --git a/docs/bin/start.sh b/docs/bin/start.sh
new file mode 100644
index 00000000..fe3579ba
--- /dev/null
+++ b/docs/bin/start.sh
@@ -0,0 +1,12 @@
+#! /bin/shell
+
+# 服务路径
+SERVER_PATH="/home/spring-boot-plus-server"
+# jar包名称
+JAR_NAME="spring-boot-plus.jar"
+# 日志文件名称
+LOG_NAME="nohup.log"
+cd ${SERVER_PATH}
+nohup java -server -Xms512m -Xmx512m -Xmn256m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m -XX:-OmitStackTraceInFastThrow -jar ${JAR_NAME} > ${LOG_NAME} 2>&1 &
+echo "${JAR_NAME}启动成功"
+tail -f -n 500 ${LOG_NAME}
\ No newline at end of file
diff --git a/docs/config/settings.xml b/docs/config/settings.xml
index aea01b88..7b474e18 100644
--- a/docs/config/settings.xml
+++ b/docs/config/settings.xml
@@ -1,23 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
-          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xmlns="http://maven.apache.org/SETTINGS/1.0.0"
           xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
     <!-- localRepository
      | The path to the local repository maven will use to store artifacts.
@@ -41,9 +25,9 @@
     <mirrors>
         <mirror>
             <id>aliyun-maven</id>
-            <name>aliyun maven</name>
-            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
             <mirrorOf>central</mirrorOf>
+            <name>aliyun maven</name>
+            <url>http://maven.aliyun.com/repository/public</url>
         </mirror>
     </mirrors>
 
diff --git a/docs/db/mysql_spring_boot_plus.sql b/docs/db/mysql_spring_boot_plus.sql
deleted file mode 100644
index 1c9f6167..00000000
--- a/docs/db/mysql_spring_boot_plus.sql
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
-*/
-
-create database if not exists spring_boot_plus character set utf8mb4 COLLATE utf8mb4_general_ci;
-
-use spring_boot_plus;
-
-create table if not exists foo_bar
-(
-    id bigint auto_increment comment 'ID'
-        primary key,
-    name varchar(20) not null comment 'Name',
-    foo varchar(20) null comment 'Foo',
-    bar varchar(20) not null comment 'Bar',
-    remark varchar(200) null comment 'Remark',
-    state int default 1 not null comment 'State，0：Disable，1：Enable',
-    version int default 0 not null comment 'Version',
-    create_time timestamp default CURRENT_TIMESTAMP null comment 'Create Time',
-    update_time timestamp null comment 'Update Time'
-)
-    comment 'FooBar';
-
-create table if not exists example_order
-(
-    id bigint auto_increment comment '主键'
-        primary key,
-    name varchar(20) not null comment '订单名称',
-    order_no varchar(100) null comment '订单编号',
-    remark varchar(200) null comment '备注',
-    state int default 1 not null comment '状态，0：禁用，1：启用',
-    version int default 0 not null comment '版本',
-    create_time timestamp default CURRENT_TIMESTAMP null comment '创建时间',
-    update_time timestamp null comment '修改时间'
-)
-    comment '订单示例';
-
-create table if not exists ip_address
-(
-    id bigint auto_increment
-        primary key,
-    ip_start varchar(15) not null,
-    ip_end varchar(15) not null,
-    area varchar(45) null comment '区域',
-    operator varchar(6) null comment '运营商',
-    ip_start_num bigint(10) not null,
-    ip_end_num bigint(10) not null
-)
-    comment 'IP地址';
-
-create index ip_address_ip_end_num_index
-    on ip_address (ip_end_num);
-
-create index ip_address_ip_start_num_index
-    on ip_address (ip_start_num);
-
-create table if not exists sys_department
-(
-    id bigint auto_increment comment '主键'
-        primary key,
-    name varchar(32) not null comment '部门名称',
-    parent_id bigint null comment '父id',
-    level int null comment '部门层级',
-    state int default 1 not null comment '状态，0：禁用，1：启用',
-    sort int default 0 not null comment '排序',
-    remark varchar(200) null comment '备注',
-    version int default 0 not null comment '版本',
-    create_time timestamp default CURRENT_TIMESTAMP null comment '创建时间',
-    update_time timestamp null comment '修改时间',
-    constraint sys_department_name_uindex
-        unique (name)
-)
-    comment '部门';
-
-create table if not exists sys_permission
-(
-    id bigint auto_increment comment '主键'
-        primary key,
-    name varchar(32) null comment '权限名称',
-    parent_id bigint null comment '父id',
-    url varchar(200) null comment '路径',
-    code varchar(100) not null comment '唯一编码',
-    icon varchar(100) null comment '图标',
-    type int not null comment '类型，1：菜单，2：按钮',
-    level int not null comment '层级，1：第一级，2：第二级，N：第N级',
-    state int default 1 not null comment '状态，0：禁用，1：启用',
-    sort int default 0 not null comment '排序',
-    remark varchar(200) null comment '备注',
-    version int default 0 not null comment '版本',
-    create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间',
-    update_time timestamp null comment '修改时间',
-    constraint sys_permission_code_uindex
-        unique (code)
-)
-    comment '系统权限';
-
-create table if not exists sys_role
-(
-    id bigint auto_increment comment '主键'
-        primary key,
-    name varchar(32) not null comment '角色名称',
-    code varchar(100) null comment '角色唯一编码',
-    type int null comment '角色类型',
-    state int default 1 not null comment '角色状态，0：禁用，1：启用',
-    remark varchar(200) null comment '备注',
-    version int default 0 not null comment '版本',
-    create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间',
-    update_time timestamp null comment '修改时间',
-    constraint sys_role_name_uindex
-        unique (name)
-)
-    comment '系统角色';
-
-create table if not exists sys_role_permission
-(
-    id bigint auto_increment comment '主键'
-        primary key,
-    role_id bigint not null comment '角色id',
-    permission_id bigint not null comment '权限id',
-    state int default 1 not null comment '状态，0：禁用，1：启用',
-    remark varchar(200) null comment '备注',
-    version int default 0 not null comment '版本',
-    create_time timestamp default CURRENT_TIMESTAMP not null comment '创建时间',
-    update_time timestamp null comment '修改时间'
-)
-    comment '角色权限关系';
-
-create index permission_id
-    on sys_role_permission (permission_id);
-
-create index role_id
-    on sys_role_permission (role_id);
-
-create table if not exists sys_user
-(
-    id bigint auto_increment comment '主键'
-        primary key,
-    username varchar(20) not null comment '用户名',
-    nickname varchar(20) null comment '昵称',
-    password varchar(64) not null comment '密码',
-    salt varchar(32) null comment '盐值',
-    phone varchar(20) not null comment '手机号码',
-    gender int default 1 not null comment '性别，0：女，1：男，默认1',
-    head varchar(200) null comment '头像',
-    remark varchar(200) null comment '备注',
-    state int default 1 not null comment '状态，0：禁用，1：启用，2：锁定',
-    department_id bigint not null comment '部门id',
-    role_id bigint not null comment '角色id',
-    deleted int default 0 not null comment '逻辑删除，0：未删除，1：已删除',
-    version int default 0 not null comment '版本',
-    create_time timestamp default CURRENT_TIMESTAMP null comment '创建时间',
-    update_time timestamp null comment '修改时间',
-    constraint sys_user_username_uindex
-        unique (username)
-)
-    comment '系统用户';
-
-create index department_id
-    on sys_user (department_id);
-
-create index role_id
-    on sys_user (role_id);
-
-create table if not exists sys_login_log
-(
-    id bigint(18) auto_increment comment '主键'
-        primary key,
-    request_id varchar(32) null comment '请求ID',
-    username varchar(32) null comment '用户名称',
-    ip varchar(15) null comment 'IP',
-    area varchar(45) null comment '区域',
-    operator varchar(6) null comment '运营商',
-    token varchar(32) null comment 'tokenMd5值',
-    type int null comment '1:登录，2：登出',
-    success tinyint(1) default 0 not null comment '是否成功 true:成功/false:失败',
-    code int null comment '响应码',
-    exception_message varchar(300) null comment '失败消息记录',
-    user_agent varchar(300) null comment '浏览器名称',
-    browser_name varchar(100) null comment '浏览器名称',
-    browser_version varchar(100) null comment '浏览器版本',
-    engine_name varchar(100) null comment '浏览器引擎名称',
-    engine_version varchar(100) null comment '浏览器引擎版本',
-    os_name varchar(100) null comment '系统名称',
-    platform_name varchar(100) null comment '平台名称',
-    mobile tinyint(1) null comment '是否是手机,0:否,1:是',
-    device_name varchar(100) null comment '移动端设备名称',
-    device_model varchar(100) null comment '移动端设备型号',
-    remark varchar(200) null comment '备注',
-    create_time datetime default CURRENT_TIMESTAMP null comment '创建时间',
-    update_time datetime null comment '修改时间'
-)
-    comment '系统登录日志';
-
-create table if not exists sys_operation_log
-(
-    id bigint(18) auto_increment comment '主键'
-        primary key,
-    request_id varchar(32) null comment '请求ID',
-    user_id bigint(18) null comment '用户ID',
-    user_name varchar(32) null comment '用户名称',
-    name varchar(200) null comment '日志名称',
-    ip varchar(15) null comment 'IP',
-    area varchar(45) null comment '区域',
-    operator varchar(6) null comment '运营商',
-    path varchar(500) null comment '全路径',
-    module varchar(100) null comment '模块名称',
-    class_name varchar(100) null comment '类名',
-    method_name varchar(100) null comment '方法名称',
-    request_method varchar(10) null comment '请求方式，GET/POST',
-    content_type varchar(100) null comment '内容类型',
-    request_body tinyint(1) null comment '是否是JSON请求映射参数',
-    param text null comment '请求参数',
-    token varchar(32) null comment 'tokenMd5值',
-    type int null comment '0:其它,1:新增,2:修改,3:删除,4:详情查询,5:所有列表,6:分页列表,7:其它查询,8:上传文件',
-    success tinyint(1) null comment '0:失败,1:成功',
-    code int null comment '响应结果状态码',
-    message varchar(100) null comment '响应结果消息',
-    exception_name varchar(200) null comment '异常类名称',
-    exception_message varchar(300) null comment '异常信息',
-    browser_name varchar(100) null comment '浏览器名称',
-    browser_version varchar(100) null comment '浏览器版本',
-    engine_name varchar(100) null comment '浏览器引擎名称',
-    engine_version varchar(100) null comment '浏览器引擎版本',
-    os_name varchar(100) null comment '系统名称',
-    platform_name varchar(100) null comment '平台名称',
-    mobile tinyint(1) null comment '是否是手机,0:否,1:是',
-    device_name varchar(100) null comment '移动端设备名称',
-    device_model varchar(100) null comment '移动端设备型号',
-    remark varchar(200) null comment '备注',
-    create_time datetime default CURRENT_TIMESTAMP null comment '创建时间',
-    update_time datetime null comment '修改时间'
-)
-    comment '系统操作日志';
-
-
-
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (1, 'admin', '管理员', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889900', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 1, 1, 1, 0, 1, '2020-02-26 00:00:00', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (2, 'test', '测试人员1', '34783fb724b259beb71a1279f7cd93bdcfd92a273d566f926419a37825c500df', '087c2e9857f35f1e243367f3b89b81c1', '15888889901', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Tester Account', 1, 1, 2, 0, 1, '2020-02-26 00:00:01', '2020-02-15 19:31:50');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (3, 'admin1', '管理员1', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889902', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 1, 1, 1, 0, 1, '2020-02-26 00:09:09', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (4, 'admin2', '管理员2', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889903', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 1, 1, 1, 0, 1, '2020-02-26 16:10:06', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (5, 'admin3', '管理员3', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889904', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 1, 1, 1, 0, 1, '2020-02-26 16:10:06', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (6, 'admin4', '管理员4', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889905', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 1, 1, 1, 0, 1, '2020-02-26 16:10:06', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (7, 'admin5', '管理员5', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889906', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 1, 1, 1, 0, 1, '2020-02-26 23:59:59', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (8, 'admin6', '管理员6', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 1, 1, 1, 0, 1, '2020-02-26 23:59:59', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (9, 'admin7', '管理员7', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 1, 1, 1, 0, 1, '2020-02-20 23:59:59', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (10, 'admin8', '管理员8', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 1, 1, 1, 0, 1, '2020-01-30 22:56:55', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (11, 'admin9', '管理员9', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 1, 1, 1, 0, 1, '2019-12-30 00:52:01', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (12, 'admin10', '管理员10', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 1, 1, 1, 0, 1, '2019-08-26 00:52:01', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (13, 'admin11', '管理员11', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 1, 1, 1, 0, 1, '2019-08-26 00:52:01', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (14, 'admin12', '管理员12', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 2, 1, 1, 0, 2, '2019-08-26 00:52:01', '2020-02-27 14:05:40');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (15, 'admin13', '管理员13uuu', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 2, 1, 1, 0, 3, '2019-08-26 00:52:01', '2020-02-27 14:05:18');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (16, 'admin14', '管理员14', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 0, 1, 1, 0, 1, '2019-08-26 00:52:01', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (17, 'admin15', '管理员15', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 0, 1, 1, 0, 1, '2019-08-26 00:52:01', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (18, 'admin16', '管理员16', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 0, 1, 1, 0, 1, '2019-08-26 00:52:01', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (19, 'admin17', '管理员17', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 2, 1, 1, 0, 1, '2019-08-26 00:52:01', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (20, 'admin18', '管理员18', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 2, 1, 1, 0, 1, '2019-08-26 00:52:01', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (21, 'admin19', '管理员19', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 2, 1, 1, 0, 1, '2019-08-26 00:52:01', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (22, 'admin20', '管理员20', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 2, 1, 1, 0, 1, '2019-08-26 00:52:01', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (23, 'admin21', '管理员21', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 0, 1, 1, 0, 1, '2019-08-26 00:52:01', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (24, 'admin22', '管理员22', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 0, 1, 1, 0, 1, '2019-08-26 00:52:01', '2019-10-27 23:32:29');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (25, 'admin23', '管理员23', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '15888889999', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'Administrator Account', 2, 1, 1, 0, 3, '2019-08-26 00:52:01', '2020-02-27 14:42:28');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (100, 'dddd', 'ddddd', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', 'aa98a65fa53d198f38d8e3a63f3f5a65', 'ddddddddddd', 1, 'http://localhost:8888/api/resource/201908201013068.png', 'dddddddd', 1, 1, 1, 0, 1, '2020-02-26 14:06:53', '2020-02-27 14:06:52');
-INSERT INTO sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (101, 'adminx', '111111', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '1faf81180b4a4a78c48d7c31479a0622', '11111111111', 1, 'http://localhost:8888/api/resource/201908201013068.png', '1111111111', 1, 1, 1, 1, 6, '2020-02-26 14:19:57', '2020-03-02 17:33:48');
-
-INSERT INTO sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (1, '技术部', null, 1, 1, 359544077, 'fe8c9cbac0c54395ac411335a31f4888', 15, '2019-10-25 09:46:49', '2019-11-13 19:56:07');
-INSERT INTO sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (2, '研发部', null, 1, 1, 0, null, 0, '2019-11-01 20:45:43', null);
-INSERT INTO sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (20, '前端开发部', 2, 2, 1, 0, null, 0, '2019-11-01 20:48:38', null);
-INSERT INTO sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (21, '后台开发部', 2, 2, 1, 0, null, 0, '2019-11-01 20:48:38', null);
-INSERT INTO sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (22, '测试部', 2, 2, 1, 0, null, 0, '2019-11-01 20:48:38', null);
-INSERT INTO sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (201, '前端一组', 20, 3, 1, 0, null, 0, '2019-11-01 20:48:38', null);
-INSERT INTO sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (202, '前端二组', 20, 3, 1, 0, null, 0, '2019-11-01 20:48:38', null);
-INSERT INTO sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (203, '后台一组', 21, 3, 1, 0, null, 0, '2019-11-01 20:48:38', null);
-INSERT INTO sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (204, '后台二组', 21, 3, 1, 0, null, 0, '2019-11-01 20:48:38', null);
-INSERT INTO sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (205, '测试一组', 22, 3, 1, 0, null, 0, '2019-11-01 20:48:38', null);
-
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (1, '管理员', 'admin', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (2, 'test', 'test', null, 1, '测试人员拥有部分权限', 0, '2019-10-25 09:48:02', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (3, '管理员1', 'admin1', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (4, '管理员2', 'admin2', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (5, '管理员3', 'admin3', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (6, '管理员4', 'admin4', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (7, '管理员5', 'admin5', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (8, '管理员6', 'admin6', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (9, '管理员7', 'admin7', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (10, '管理员8', 'admin8', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (11, '管理员9', 'admin9', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (12, '管理员10', 'admin10', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (13, '管理员11', 'admin11', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (14, '管理员12', 'admin12', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (15, '管理员13', 'admin13', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (16, '管理员14', 'admin14', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (17, '管理员15', 'admin15', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (18, '管理员16', 'admin16', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (19, '管理员17', 'admin17', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (20, '管理员18', 'admin18', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (21, '管理员19', 'admin19', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (22, '管理员20', 'admin20', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-INSERT INTO sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (23, '管理员21', 'admin21', null, 1, '管理员拥有所有权限', 0, '2019-10-25 09:47:21', null);
-
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1, '系统管理', null, null, 'system:management', 'el-icon-s-unfold', 1, 1, 1, 0, '1权限备注', 0, '2019-10-26 11:12:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (100, '用户管理', 1, null, 'sys:user:management', 'el-icon-s-unfold', 1, 2, 1, 0, '100权限备注', 0, '2019-10-26 11:15:48', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (200, '角色管理', 1, null, 'sys:role:management', 'el-icon-s-unfold', 1, 2, 1, 0, '200权限备注', 0, '2019-10-26 11:15:48', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (300, '权限管理', 1, null, 'sys:permission:management', 'el-icon-s-unfold', 1, 2, 1, 0, '300权限备注', 0, '2019-10-26 11:15:48', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (400, '部门管理', 1, null, 'sys:department:management', 'el-icon-s-unfold', 1, 2, 1, 0, '400权限备注', 0, '2019-10-26 11:15:48', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (500, '日志管理', 1, null, 'sys:log:manager', 'el-icon-s-custom', 1, 2, 1, 0, '500权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1000, '用户新增', 100, null, 'sys:user:add', 'el-icon-s-custom', 2, 3, 1, 0, '1000权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1001, '用户修改', 100, null, 'sys:user:update', 'el-icon-s-custom', 2, 3, 1, 0, '1001权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1002, '用户删除', 100, null, 'sys:user:delete', 'el-icon-s-custom', 2, 3, 1, 0, '1002权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1003, '用户详情', 100, null, 'sys:user:info', 'el-icon-s-custom', 2, 3, 1, 0, '1003权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1004, '用户分页列表', 100, null, 'sys:user:page', 'el-icon-s-custom', 2, 3, 1, 0, '1004权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1005, '用户修改密码', 100, null, 'sys:user:update:password', 'el-icon-s-custom', 2, 3, 1, 0, '1005权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1006, '用户修改头像', 100, null, 'sys:user:update:head', 'el-icon-s-custom', 2, 3, 1, 0, '1006权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1007, '用户重置密码', 100, null, 'sys:user:reset:password', 'el-icon-s-custom', 2, 3, 1, 0, '1007权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (2000, '角色新增', 200, null, 'sys:role:add', 'el-icon-s-custom', 2, 3, 1, 0, '2000权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (2001, '角色修改', 200, null, 'sys:role:update', 'el-icon-s-custom', 2, 3, 1, 0, '2001权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (2002, '角色删除', 200, null, 'sys:role:delete', 'el-icon-s-custom', 2, 3, 1, 0, '2002权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (2003, '角色详情', 200, null, 'sys:role:info', 'el-icon-s-custom', 2, 3, 1, 0, '2003权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (2004, '角色分页列表', 200, null, 'sys:role:page', 'el-icon-s-custom', 2, 3, 1, 0, '2004权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (2005, '角色列表', 200, null, 'sys:role:list', 'el-icon-s-custom', 2, 3, 1, 0, '2005权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (2006, '角色权限ID列表', 200, null, 'sys:permission:three-ids-by-role-id', 'el-icon-s-custom', 2, 3, 1, 0, '2006权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3000, '权限新增', 300, null, 'sys:permission:add', 'el-icon-s-custom', 2, 3, 1, 0, '3000权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3001, '权限修改', 300, null, 'sys:permission:update', 'el-icon-s-custom', 2, 3, 1, 0, '3001权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3002, '权限删除', 300, null, 'sys:permission:delete', 'el-icon-s-custom', 2, 3, 1, 0, '3002权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3003, '权限详情', 300, null, 'sys:permission:info', 'el-icon-s-custom', 2, 3, 1, 0, '3003权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3004, '权限分页列表', 300, null, 'sys:permission:page', 'el-icon-s-custom', 2, 3, 1, 0, '3004权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3005, '权限所有列表', 300, null, 'sys:permission:all:menu:list', 'el-icon-s-custom', 2, 3, 1, 0, '3005权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3006, '权限所有树形列表', 300, null, 'sys:permission:all:menu:tree', 'el-icon-s-custom', 2, 3, 1, 0, '3006权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3007, '权限用户列表', 300, null, 'sys:permission:menu:list', 'el-icon-s-custom', 2, 3, 1, 0, '3007权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3008, '权限用户树形列表', 300, null, 'sys:permission:menu:tree', 'el-icon-s-custom', 2, 3, 1, 0, '3008权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3009, '权限用户代码列表', 300, null, 'sys:permission:codes', 'el-icon-s-custom', 2, 3, 1, 0, '3009权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3010, '导航菜单', 300, null, 'sys:permission:nav-menu', 'el-icon-s-custom', 2, 3, 1, 0, '3010权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3011, '角色权限修改', 300, null, 'sys:role-permission:update', 'el-icon-s-custom', 2, 3, 1, 0, '3011权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (4000, '部门新增', 400, null, 'sys:department:add', 'el-icon-s-custom', 2, 3, 1, 0, '4000权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (4001, '部门修改', 400, null, 'sys:department:update', 'el-icon-s-custom', 2, 3, 1, 0, '4001权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (4002, '部门删除', 400, null, 'sys:department:delete', 'el-icon-s-custom', 2, 3, 1, 0, '4002权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (4003, '部门详情', 400, null, 'sys:department:info', 'el-icon-s-custom', 2, 3, 1, 0, '4003权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (4004, '部门分页列表', 400, null, 'sys:department:page', 'el-icon-s-custom', 2, 3, 1, 0, '4004权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (4005, '部门列表', 400, null, 'sys:department:list', 'el-icon-s-custom', 2, 3, 1, 0, '4005权限备注', 1, '2019-10-26 11:18:40', '2020-03-09 00:50:13');
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (4006, '部门树形列表', 400, null, 'sys:department:all:tree', 'el-icon-s-custom', 2, 3, 1, 0, '4006权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (5001, '操作日志列表', 500, null, 'sys:operation:log:page', 'el-icon-s-custom', 2, 3, 1, 0, '5001权限备注', 0, '2019-10-26 11:18:40', null);
-INSERT INTO sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (5002, '登录日志列表', 500, null, 'sys:login:log:page', 'el-icon-s-custom', 2, 3, 1, 0, '5002权限备注', 0, '2019-10-26 11:18:40', null);
-
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (303, 1, 3008, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (304, 1, 1, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (305, 1, 3009, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (306, 1, 3010, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (307, 1, 3011, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (308, 1, 200, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (309, 1, 5001, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (310, 1, 5002, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (311, 1, 2000, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (312, 1, 400, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (313, 1, 2001, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (314, 1, 2002, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (315, 1, 2003, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (316, 1, 2004, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (317, 1, 2005, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (318, 1, 2006, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (319, 1, 4000, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (320, 1, 4001, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (321, 1, 4002, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (322, 1, 4003, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (323, 1, 100, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (324, 1, 4004, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (325, 1, 4005, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (326, 1, 4006, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (327, 1, 1000, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (328, 1, 1001, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (329, 1, 1002, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (330, 1, 1003, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (331, 1, 1004, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (332, 1, 300, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (333, 1, 1005, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (334, 1, 1006, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (335, 1, 1007, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (336, 1, 500, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (337, 1, 3000, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (338, 1, 3001, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (339, 1, 3002, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (340, 1, 3003, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (341, 1, 3004, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (342, 1, 3005, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (343, 1, 3006, 1, null, 0, '2020-04-01 00:14:36', null);
-INSERT INTO sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (344, 1, 3007, 1, null, 0, '2020-04-01 00:14:36', null);
-
-INSERT INTO foo_bar (id, name, foo, bar, remark, state, version, create_time, update_time) VALUES (1, 'test add', 'hello', 'world', '备注', 1, 0, '2020-03-20 11:22:35', null);
-
-INSERT INTO example_order (id, name, order_no, remark, state, version, create_time, update_time) VALUES (1, 'AAA', null, null, 1, 0, '2020-03-12 22:25:35', null);
-INSERT INTO example_order (id, name, order_no, remark, state, version, create_time, update_time) VALUES (2, 'BBB', null, null, 1, 0, '2020-03-12 22:25:35', null);
-INSERT INTO example_order (id, name, order_no, remark, state, version, create_time, update_time) VALUES (3, 'CCC', null, null, 1, 0, '2020-03-12 22:25:35', null);
-
-INSERT INTO sys_login_log (id, request_id, username, ip, area, operator, token, type, success, code, exception_message, user_agent, browser_name, browser_version, engine_name, engine_version, os_name, platform_name, mobile, device_name, device_model, remark, create_time, update_time) VALUES (1, '1242813712335691777', 'admin', '127.0.0.1', '本机地址', null, 'c87aaffa35dadafb066cf18679eab36e', 1, 1, 200, null, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36', 'Chrome', '80.0.3987.149', 'Webkit', '537.36', 'OSX', 'Mac', 0, null, null, null, '2020-03-25 22:01:11', null);
-INSERT INTO sys_login_log (id, request_id, username, ip, area, operator, token, type, success, code, exception_message, user_agent, browser_name, browser_version, engine_name, engine_version, os_name, platform_name, mobile, device_name, device_model, remark, create_time, update_time) VALUES (2, '1242813887884091393', 'admin', '127.0.0.1', '本机地址', null, 'c87aaffa35dadafb066cf18679eab36e', 2, 1, 200, null, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36', 'Chrome', '80.0.3987.149', 'Webkit', '537.36', 'OSX', 'Mac', 0, null, null, null, '2020-03-25 22:01:48', null);
-INSERT INTO sys_login_log (id, request_id, username, ip, area, operator, token, type, success, code, exception_message, user_agent, browser_name, browser_version, engine_name, engine_version, os_name, platform_name, mobile, device_name, device_model, remark, create_time, update_time) VALUES (3, '1242814069371625474', 'admin', '127.0.0.1', '本机地址', null, null, 1, 0, null, '用户名或密码错误', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36', 'Chrome', '80.0.3987.149', 'Webkit', '537.36', 'OSX', 'Mac', 0, null, null, null, '2020-03-25 22:02:25', null);
-INSERT INTO sys_login_log (id, request_id, username, ip, area, operator, token, type, success, code, exception_message, user_agent, browser_name, browser_version, engine_name, engine_version, os_name, platform_name, mobile, device_name, device_model, remark, create_time, update_time) VALUES (4, '1242814192096960513', null, '127.0.0.1', '本机地址', null, null, 2, 0, null, 'token不能为空', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36', 'Chrome', '80.0.3987.149', 'Webkit', '537.36', 'OSX', 'Mac', 0, null, null, null, '2020-03-25 22:02:54', null);
-
-INSERT INTO sys_operation_log (id, request_id, user_id, user_name, name, ip, area, operator, path, module, class_name, method_name, request_method, content_type, request_body, param, token, type, success, code, message, exception_name, exception_message, browser_name, browser_version, engine_name, engine_version, os_name, platform_name, mobile, device_name, device_model, remark, create_time, update_time) VALUES (1, '1242805276474634241', null, null, 'helloWorld', '127.0.0.1', '本机地址', null, '/api/hello/world', null, 'io.geekidea.springbootplus.system.controller.HelloWorldController', 'helloWorld', 'GET', null, 0, null, null, 0, 1, 200, '操作成功', null, null, 'Chrome', '80.0.3987.149', 'Webkit', '537.36', 'OSX', 'Mac', 0, null, null, '', '2020-03-25 21:27:22', null);
-INSERT INTO sys_operation_log (id, request_id, user_id, user_name, name, ip, area, operator, path, module, class_name, method_name, request_method, content_type, request_body, param, token, type, success, code, message, exception_name, exception_message, browser_name, browser_version, engine_name, engine_version, os_name, platform_name, mobile, device_name, device_model, remark, create_time, update_time) VALUES (2, '1242820418688049153', null, null, 'FooBar分页列表', '127.0.0.1', '本机地址', null, '/api/fooBar/getPageList', 'foobar', 'com.example.foobar.controller.FooBarController', 'getFooBarPageList', 'POST', 'application/json', 1, '{"pageIndex":1,"pageSize":10}', null, 7, 1, 200, '操作成功', null, null, 'Chrome', '80.0.3987.149', 'Webkit', '537.36', 'OSX', 'Mac', 0, null, null, '', '2020-03-25 22:27:33', null);
diff --git a/docs/db/oracle_spring_boot_plus.sql b/docs/db/oracle_spring_boot_plus.sql
deleted file mode 100644
index a2dd2115..00000000
--- a/docs/db/oracle_spring_boot_plus.sql
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
-*/
-
-create table IP
-(
-    ID           NUMBER(20)    not null
-        constraint IP_PK
-        primary key,
-    IP_START     VARCHAR2(15)  not null,
-    IP_END       VARCHAR2(15)  not null,
-    AREA         VARCHAR2(100) not null,
-    OPERATOR     VARCHAR2(200) not null,
-    IP_START_NUM NUMBER(20)    not null,
-    IP_END_NUM   NUMBER(20)    not null
-)
-    /
-
-create table SYS_LOG
-(
-    LOG_ID      NUMBER(20) not null
-        constraint SYS_LOG_PK
-        primary key,
-    TYPE        NUMBER(1),
-    CONTENT     VARCHAR2(255),
-    CREATE_ID   NUMBER(20),
-    CREATE_TIME DATE default sysdate
-)
-    /
-
-comment on table SYS_LOG is '系统日志'
-/
-
-comment on column SYS_LOG.LOG_ID is '主键'
-/
-
-comment on column SYS_LOG.CONTENT is '内容'
-/
-
-comment on column SYS_LOG.CREATE_ID is '创建人ID'
-/
-
-comment on column SYS_LOG.CREATE_TIME is '创建时间'
-/
-
-create table FOO_BAR
-(
-    ID          NUMBER(20)   not null
-        constraint FOO_BAR_PK
-        primary key,
-    NAME        VARCHAR2(20) not null,
-    FOO         VARCHAR2(20),
-    BAR         VARCHAR2(20) not null,
-    REMARK      VARCHAR2(200),
-    STATE       NUMBER(11) default '1',
-    VERSION     NUMBER(11) default '0',
-    CREATE_TIME DATE       default sysdate,
-    UPDATE_TIME DATE
-)
-    /
-
-comment on table FOO_BAR is 'FooBar'
-/
-
-comment on column FOO_BAR.ID is '主键'
-/
-
-comment on column FOO_BAR.NAME is '名称'
-/
-
-comment on column FOO_BAR.FOO is 'Foo'
-/
-
-comment on column FOO_BAR.BAR is 'Bar'
-/
-
-comment on column FOO_BAR.REMARK is '备注'
-/
-
-comment on column FOO_BAR.STATE is '状态，0：禁用，1：启用'
-/
-
-comment on column FOO_BAR.VERSION is '版本'
-/
-
-comment on column FOO_BAR.CREATE_TIME is '创建时间'
-/
-
-comment on column FOO_BAR.UPDATE_TIME is '修改时间'
-/
-
-create table SYS_DEPARTMENT
-(
-    ID          NUMBER(20)   not null,
-    NAME        VARCHAR2(32) not null,
-    PARENT_ID   NUMBER(20),
-    "LEVEL"     NUMBER(2),
-    STATE       NUMBER(11) default '1',
-    SORT        NUMBER(11) default '0',
-    REMARK      VARCHAR2(200),
-    VERSION     NUMBER(11) default '0',
-    CREATE_TIME DATE       default sysdate,
-    UPDATE_TIME DATE
-)
-    /
-
-comment on table SYS_DEPARTMENT is '部门'
-/
-
-comment on column SYS_DEPARTMENT.ID is '主键'
-/
-
-comment on column SYS_DEPARTMENT.NAME is '部门名称'
-/
-
-comment on column SYS_DEPARTMENT.PARENT_ID is '父id'
-/
-
-comment on column SYS_DEPARTMENT."LEVEL" is '部门层级'
-/
-
-comment on column SYS_DEPARTMENT.STATE is '状态，0：禁用，1：启用'
-/
-
-comment on column SYS_DEPARTMENT.SORT is '排序'
-/
-
-comment on column SYS_DEPARTMENT.REMARK is '备注'
-/
-
-comment on column SYS_DEPARTMENT.VERSION is '版本'
-/
-
-comment on column SYS_DEPARTMENT.CREATE_TIME is '创建时间'
-/
-
-comment on column SYS_DEPARTMENT.UPDATE_TIME is '修改时间'
-/
-
-create unique index SYS_DEPARTMENT_NAME_UINDEX
-    on SYS_DEPARTMENT (NAME)
-    /
-
-create table SYS_USER
-(
-    ID            NUMBER(20)   not null
-        constraint SYS_USER_PK
-        primary key,
-    USERNAME      VARCHAR2(20) not null,
-    NICKNAME      VARCHAR2(20),
-    PASSWORD      VARCHAR2(64) not null,
-    SALT          VARCHAR2(32),
-    PHONE         VARCHAR2(20) not null,
-    GENDER        NUMBER(11) default '1',
-    HEAD          VARCHAR2(200),
-    REMARK        VARCHAR2(200),
-    STATE         NUMBER(11) default '1',
-    DEPARTMENT_ID NUMBER(20)   not null,
-    ROLE_ID       NUMBER(20)   not null,
-    DELETED       NUMBER(11) default '0',
-    VERSION       NUMBER(11) default '0',
-    CREATE_TIME   DATE       default sysdate,
-    UPDATE_TIME   DATE
-)
-    /
-
-comment on table SYS_USER is '系统用户'
-/
-
-comment on column SYS_USER.ID is '主键'
-/
-
-comment on column SYS_USER.USERNAME is '用户名'
-/
-
-comment on column SYS_USER.NICKNAME is '昵称'
-/
-
-comment on column SYS_USER.PASSWORD is '密码'
-/
-
-comment on column SYS_USER.SALT is '盐值'
-/
-
-comment on column SYS_USER.PHONE is '手机号码'
-/
-
-comment on column SYS_USER.GENDER is '性别，0：女，1：男，默认1'
-/
-
-comment on column SYS_USER.HEAD is '头像'
-/
-
-comment on column SYS_USER.REMARK is 'remark'
-/
-
-comment on column SYS_USER.STATE is '状态，0：禁用，1：启用，2：锁定'
-/
-
-comment on column SYS_USER.DEPARTMENT_ID is '部门id'
-/
-
-comment on column SYS_USER.ROLE_ID is '角色id'
-/
-
-comment on column SYS_USER.DELETED is '逻辑删除，0：未删除，1：已删除'
-/
-
-comment on column SYS_USER.VERSION is '版本'
-/
-
-comment on column SYS_USER.CREATE_TIME is '创建时间'
-/
-
-comment on column SYS_USER.UPDATE_TIME is '修改时间'
-/
-
-create index SYS_USER_USERNAME_INDEX
-    on SYS_USER (USERNAME)
-    /
-
-create table SYS_ROLE
-(
-    ID          NUMBER(20)    not null,
-    NAME        VARCHAR2(32)  not null,
-    CODE        VARCHAR2(100) not null,
-    TYPE        NUMBER(11),
-    STATE       NUMBER(11) default '1',
-    REMARK      VARCHAR2(200),
-    VERSION     NUMBER(11) default '0',
-    CREATE_TIME DATE       default sysdate,
-    UPDATE_TIME DATE
-)
-    /
-
-comment on table SYS_ROLE is '系统角色'
-/
-
-comment on column SYS_ROLE.ID is '主键'
-/
-
-comment on column SYS_ROLE.NAME is '角色名称'
-/
-
-comment on column SYS_ROLE.CODE is '角色唯一编码'
-/
-
-comment on column SYS_ROLE.TYPE is '角色类型'
-/
-
-comment on column SYS_ROLE.STATE is '角色状态，0：禁用，1：启用'
-/
-
-comment on column SYS_ROLE.REMARK is '备注'
-/
-
-comment on column SYS_ROLE.VERSION is '版本'
-/
-
-comment on column SYS_ROLE.CREATE_TIME is '创建时间'
-/
-
-comment on column SYS_ROLE.UPDATE_TIME is '修改时间'
-/
-
-create unique index SYS_ROLE_NAME_INDEX
-    on SYS_ROLE (NAME)
-    /
-
-create unique index SYS_ROLE_CODE_UINDEX
-    on SYS_ROLE (CODE)
-    /
-
-create table SYS_PERMISSION
-(
-    ID          NUMBER(20)    not null
-        constraint SYS_PERMISSION_PK
-        primary key,
-    NAME        VARCHAR2(32),
-    PARENT_ID   NUMBER(20),
-    URL         VARCHAR2(200),
-    CODE        VARCHAR2(100) not null,
-    ICON        VARCHAR2(100),
-    TYPE        NUMBER(11)    not null,
-    "LEVEL"     NUMBER(11)    not null,
-    STATE       NUMBER(11) default '1',
-    SORT        NUMBER(11) default '0',
-    REMARK      VARCHAR2(200),
-    VERSION     NUMBER(11) default '0',
-    CREATE_TIME DATE       default sysdate,
-    UPDATE_TIME DATE
-)
-    /
-
-comment on table SYS_PERMISSION is '系统权限'
-/
-
-comment on column SYS_PERMISSION.ID is '主键'
-/
-
-comment on column SYS_PERMISSION.NAME is '权限名称'
-/
-
-comment on column SYS_PERMISSION.PARENT_ID is '父id'
-/
-
-comment on column SYS_PERMISSION.URL is '路径'
-/
-
-comment on column SYS_PERMISSION.CODE is '唯一编码'
-/
-
-comment on column SYS_PERMISSION.ICON is '图标'
-/
-
-comment on column SYS_PERMISSION.TYPE is '类型，1：菜单，2：按钮'
-/
-
-comment on column SYS_PERMISSION."LEVEL" is '层级，1：第一级，2：第二级，N：第N级'
-/
-
-comment on column SYS_PERMISSION.STATE is '状态，0：禁用，1：启用'
-/
-
-comment on column SYS_PERMISSION.SORT is '排序'
-/
-
-comment on column SYS_PERMISSION.REMARK is '备注'
-/
-
-comment on column SYS_PERMISSION.VERSION is '版本'
-/
-
-comment on column SYS_PERMISSION.CREATE_TIME is '创建时间'
-/
-
-comment on column SYS_PERMISSION.UPDATE_TIME is '修改时间'
-/
-
-create index SYS_PERMISSION_CODE_INDEX
-    on SYS_PERMISSION (CODE)
-    /
-
-create table SYS_ROLE_PERMISSION
-(
-    ID            NUMBER(20) not null
-        constraint SYS_ROLE_PERMISSION_PK
-        primary key,
-    ROLE_ID       NUMBER(20) not null,
-    PERMISSION_ID NUMBER(20) not null,
-    STATE         NUMBER(11) default '1',
-    REMARK        VARCHAR2(200),
-    VERSION       NUMBER(11) default '0',
-    CREATE_TIME   DATE       default sysdate,
-    UPDATE_TIME   DATE
-)
-    /
-
-comment on table SYS_ROLE_PERMISSION is '角色权限关系'
-/
-
-comment on column SYS_ROLE_PERMISSION.ID is '主键'
-/
-
-comment on column SYS_ROLE_PERMISSION.ROLE_ID is '角色id'
-/
-
-comment on column SYS_ROLE_PERMISSION.PERMISSION_ID is '权限id'
-/
-
-comment on column SYS_ROLE_PERMISSION.STATE is '状态，0：禁用，1：启用'
-/
-
-comment on column SYS_ROLE_PERMISSION.REMARK is '备注'
-/
-
-comment on column SYS_ROLE_PERMISSION.VERSION is '版本'
-/
-
-comment on column SYS_ROLE_PERMISSION.CREATE_TIME is '创建时间'
-/
-
-comment on column SYS_ROLE_PERMISSION.UPDATE_TIME is '修改时间'
-/
-
-create index SYS_ROLE_PERMISSION_ID_ID_IN
-    on SYS_ROLE_PERMISSION (ROLE_ID, PERMISSION_ID)
-    /
-
-
--- Create sequence
-create sequence FOO_BAR_SEQUENCE
-minvalue 1
-maxvalue 9999999999999
-start with 1
-increment by 1
-cache 20;
-
-create sequence IP_SEQUENCE
-minvalue 1
-maxvalue 9999999999999
-start with 1
-increment by 1
-cache 20;
-
-create sequence SYS_DEPARTMENT_SEQUENCE
-minvalue 1
-maxvalue 9999999999999
-start with 1
-increment by 1
-cache 20;
-
-create sequence SYS_LOG_SEQUENCE
-minvalue 1
-maxvalue 9999999999999
-start with 1
-increment by 1
-cache 20;
-
-create sequence SYS_PERMISSION_SEQUENCE
-minvalue 1
-maxvalue 9999999999999
-start with 1
-increment by 1
-cache 20;
-
-create sequence SYS_ROLE_SEQUENCE
-minvalue 1
-maxvalue 9999999999999
-start with 1
-increment by 1
-cache 20;
-
-create sequence SYS_ROLE_PERMISSION_SEQUENCE
-minvalue 1
-maxvalue 9999999999999
-start with 1
-increment by 1
-cache 20;
-
-create sequence SYS_USER_SEQUENCE
-minvalue 1
-maxvalue 9999999999999
-start with 1
-increment by 1
-cache 20;
-
-
-
-INSERT INTO SYS_DEPARTMENT (ID, NAME, PARENT_ID, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (1, '管理部', null, 1, 1, 0, null, 0, TO_DATE('2019-10-25 09:46:49', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_DEPARTMENT (ID, NAME, PARENT_ID, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (2, '技术部', null, 1, 1, 0, null, 0, TO_DATE('2019-11-01 20:45:43', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_DEPARTMENT (ID, NAME, PARENT_ID, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (20, '前端开发部', 2, 2, 1, 0, null, 0, TO_DATE('2019-11-01 20:48:38', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_DEPARTMENT (ID, NAME, PARENT_ID, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (21, '后台开发部', 2, 2, 1, 0, null, 0, TO_DATE('2019-11-01 20:48:38', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_DEPARTMENT (ID, NAME, PARENT_ID, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (22, '测试部', 2, 2, 1, 0, null, 0, TO_DATE('2019-11-01 20:48:38', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_DEPARTMENT (ID, NAME, PARENT_ID, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (201, '前端一组', 20, 3, 1, 0, null, 0, TO_DATE('2019-11-01 20:48:38', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_DEPARTMENT (ID, NAME, PARENT_ID, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (202, '前端二组', 20, 3, 1, 0, null, 0, TO_DATE('2019-11-01 20:48:38', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_DEPARTMENT (ID, NAME, PARENT_ID, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (203, '后台一组', 21, 3, 1, 0, null, 0, TO_DATE('2019-11-01 20:48:38', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_DEPARTMENT (ID, NAME, PARENT_ID, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (204, '后台二组', 21, 3, 1, 0, null, 0, TO_DATE('2019-11-01 20:48:38', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_DEPARTMENT (ID, NAME, PARENT_ID, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (205, '测试一组', 22, 3, 1, 0, null, 0, TO_DATE('2019-11-01 20:48:38', 'YYYY-MM-DD HH24:MI:SS'), null);
-
-
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (1, '系统管理', null, null, 'system:management', null, 1, 1, 1, 0, null, 0, TO_DATE('2019-10-26 11:12:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (100, '用户管理', 1, null, 'sys:user:management', null, 1, 2, 1, 0, null, 0, TO_DATE('2019-10-26 11:15:48', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (200, '角色管理', 1, null, 'sys:role:management', null, 1, 2, 1, 0, null, 0, TO_DATE('2019-10-26 11:15:48', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (300, '权限管理', 1, null, 'sys:permission:management', null, 1, 2, 1, 0, null, 0, TO_DATE('2019-10-26 11:15:48', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (400, '部门管理', 1, null, 'sys:department:management', null, 1, 2, 1, 0, null, 0, TO_DATE('2019-10-26 11:15:48', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (1000, '用户新增', 100, null, 'sys:user:add', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (1001, '用户修改', 100, null, 'sys:user:update', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (1002, '用户删除', 100, null, 'sys:user:delete', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (1003, '用户详情', 100, null, 'sys:user:info', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (1004, '用户分页列表', 100, null, 'sys:user:page', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (1005, '用户修改密码', 100, null, 'sys:user:update:password', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (1006, '用户修改头像', 100, null, 'sys:user:update:head', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (2000, '角色新增', 200, null, 'sys:role:add', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (2001, '角色修改', 200, null, 'sys:role:update', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (2002, '角色删除', 200, null, 'sys:role:delete', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (2003, '角色详情', 200, null, 'sys:role:info', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (2004, '角色分页列表', 200, null, 'sys:role:page', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (3000, '权限新增', 300, null, 'sys:permission:add', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (3001, '权限修改', 300, null, 'sys:permission:update', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (3002, '权限删除', 300, null, 'sys:permission:delete', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (3003, '权限详情', 300, null, 'sys:permission:info', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (3004, '权限分页列表', 300, null, 'sys:permission:page', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (3005, '权限所有列表', 300, null, 'sys:permission:all:menu:list', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (3006, '权限所有树形列表', 300, null, 'sys:permission:all:menu:tree', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (3007, '权限用户列表', 300, null, 'sys:permission:menu:list', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (3008, '权限用户树形列表', 300, null, 'sys:permission:menu:tree', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (3009, '权限用户代码列表', 300, null, 'sys:permission:codes', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (4000, '部门新增', 400, null, 'sys:department:add', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (4001, '部门修改', 400, null, 'sys:department:update', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (4002, '部门删除', 400, null, 'sys:department:delete', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (4003, '部门详情', 400, null, 'sys:department:info', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_PERMISSION (ID, NAME, PARENT_ID, URL, CODE, ICON, TYPE, "LEVEL", STATE, SORT, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (4004, '部门分页列表', 400, null, 'sys:department:page', null, 2, 3, 1, 0, null, 0, TO_DATE('2019-10-26 11:18:40', 'YYYY-MM-DD HH24:MI:SS'), null);
-
-
-INSERT INTO SYS_ROLE (ID, NAME, CODE, TYPE, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (1, '管理员', 'admin', null, 1, null, 0, TO_DATE('2019-10-25 09:47:21', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE (ID, NAME, CODE, TYPE, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (2, 'test', 'test', null, 1, null, 0, TO_DATE('2019-10-25 09:48:02', 'YYYY-MM-DD HH24:MI:SS'), null);
-
-
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (1, 1, 1, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (2, 1, 100, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (3, 1, 200, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (4, 1, 300, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (5, 1, 400, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (6, 1, 1000, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (7, 1, 1001, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (8, 1, 1002, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (9, 1, 1003, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (10, 1, 1004, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (11, 1, 1005, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (12, 1, 1006, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (13, 1, 2000, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (14, 1, 2001, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (15, 1, 2002, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (16, 1, 2003, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (17, 1, 2004, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (18, 1, 3000, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (19, 1, 3001, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (20, 1, 3002, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (21, 1, 3003, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (22, 1, 3004, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (23, 1, 3005, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (24, 1, 3006, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (25, 1, 3007, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (26, 1, 3008, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (27, 1, 3009, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (28, 1, 4001, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (29, 1, 4002, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (30, 1, 4003, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (31, 1, 4004, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (100, 1, 1, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (101, 1, 100, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (102, 1, 1000, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (103, 1, 1001, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (104, 1, 1002, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (105, 1, 1003, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_ROLE_PERMISSION (ID, ROLE_ID, PERMISSION_ID, STATE, REMARK, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (106, 1, 1004, 1, null, 0, TO_DATE('2019-10-26 22:16:19', 'YYYY-MM-DD HH24:MI:SS'), null);
-
-
-INSERT INTO SYS_USER (ID, USERNAME, NICKNAME, PASSWORD, SALT, PHONE, GENDER, HEAD, REMARK, STATE, DEPARTMENT_ID, ROLE_ID, DELETED, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (1, 'admin', '管理员', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '1', 1, 'http://localhost:8888//resource/201910281559227.jpg', 'Administrator Account', 1, 1, 1, 0, 1, TO_DATE('2019-08-26 00:52:01', 'YYYY-MM-DD HH24:MI:SS'), null);
-INSERT INTO SYS_USER (ID, USERNAME, NICKNAME, PASSWORD, SALT, PHONE, GENDER, HEAD, REMARK, STATE, DEPARTMENT_ID, ROLE_ID, DELETED, VERSION, CREATE_TIME, UPDATE_TIME) VALUES (2, 'test', '测试人员', '34783fb724b259beb71a1279f7cd93bdcfd92a273d566f926419a37825c500df', '087c2e9857f35f1e243367f3b89b81c1', '2', 1, null, 'Tester Account', 1, 1, 2, 0, 0, TO_DATE('2019-10-05 14:04:27', 'YYYY-MM-DD HH24:MI:SS'), null);
diff --git a/docs/db/sqlserver_spring_boot_plus.sql b/docs/db/sqlserver_spring_boot_plus.sql
deleted file mode 100644
index 0afee794..00000000
--- a/docs/db/sqlserver_spring_boot_plus.sql
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
-*/
-
-create table foo_bar
-(
-    id          bigint identity
-        constraint foo_bar_pk
-        primary key nonclustered,
-    name        varchar(20)                not null,
-    foo         varchar(20),
-    bar         varchar(20)                not null,
-    remark      varchar(200),
-    state       int      default 1         not null,
-    version     int      default 0         not null,
-    create_time datetime default getdate() not null,
-    update_time datetime
-) go
-
-exec sp_addextendedproperty 'MS_Description', 'FooBar', 'SCHEMA', 'dbo', 'TABLE', 'foo_bar'
-go
-
-exec sp_addextendedproperty 'MS_Description', '主键', 'SCHEMA', 'dbo', 'TABLE', 'foo_bar', 'COLUMN', 'id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '名称', 'SCHEMA', 'dbo', 'TABLE', 'foo_bar', 'COLUMN', 'name'
-go
-
-exec sp_addextendedproperty 'MS_Description', 'Foo', 'SCHEMA', 'dbo', 'TABLE', 'foo_bar', 'COLUMN', 'foo'
-go
-
-exec sp_addextendedproperty 'MS_Description', 'Bar', 'SCHEMA', 'dbo', 'TABLE', 'foo_bar', 'COLUMN', 'bar'
-go
-
-exec sp_addextendedproperty 'MS_Description', '备注', 'SCHEMA', 'dbo', 'TABLE', 'foo_bar', 'COLUMN', 'remark'
-go
-
-exec sp_addextendedproperty 'MS_Description', '状态，0：禁用，1：启用', 'SCHEMA', 'dbo', 'TABLE', 'foo_bar', 'COLUMN', 'state'
-go
-
-exec sp_addextendedproperty 'MS_Description', '版本', 'SCHEMA', 'dbo', 'TABLE', 'foo_bar', 'COLUMN', 'version'
-go
-
-exec sp_addextendedproperty 'MS_Description', '创建时间', 'SCHEMA', 'dbo', 'TABLE', 'foo_bar', 'COLUMN', 'create_time'
-go
-
-exec sp_addextendedproperty 'MS_Description', '修改时间', 'SCHEMA', 'dbo', 'TABLE', 'foo_bar', 'COLUMN', 'update_time'
-go
-
-create table ip
-(
-    id           bigint identity
-        constraint ip_pk
-        primary key nonclustered,
-    ip_start     varchar(15)                not null,
-    ip_end       varchar(15)                not null,
-    area         varchar(100)               not null,
-    operator     varchar(200)               not null,
-    ip_start_num bigint                     not null,
-    ip_end_num   bigint                     not null,
-    create_time  datetime default getdate() not null
-) go
-
-exec sp_addextendedproperty 'MS_Description', 'IP', 'SCHEMA', 'dbo', 'TABLE', 'ip'
-go
-
-exec sp_addextendedproperty 'MS_Description', '主键', 'SCHEMA', 'dbo', 'TABLE', 'ip', 'COLUMN', 'id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '创建时间', 'SCHEMA', 'dbo', 'TABLE', 'ip', 'COLUMN', 'create_time'
-go
-
-create table sys_department
-(
-    id          bigint identity
-        constraint sys_department_pk
-        primary key nonclustered,
-    name        varchar(32)                not null,
-    parent_id   bigint,
-    level       int,
-    state       int      default 1         not null,
-    sort        int      default 0         not null,
-    remark      varchar(200),
-    version     int      default 0         not null,
-    create_time datetime default getdate() not null,
-    update_time datetime
-) go
-
-exec sp_addextendedproperty 'MS_Description', '部门', 'SCHEMA', 'dbo', 'TABLE', 'sys_department'
-go
-
-exec sp_addextendedproperty 'MS_Description', '主键', 'SCHEMA', 'dbo', 'TABLE', 'sys_department', 'COLUMN', 'id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '部门名称', 'SCHEMA', 'dbo', 'TABLE', 'sys_department', 'COLUMN', 'name'
-go
-
-exec sp_addextendedproperty 'MS_Description', '父id', 'SCHEMA', 'dbo', 'TABLE', 'sys_department', 'COLUMN', 'parent_id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '部门层级', 'SCHEMA', 'dbo', 'TABLE', 'sys_department', 'COLUMN', 'level'
-go
-
-exec sp_addextendedproperty 'MS_Description', '状态，0：禁用，1：启用', 'SCHEMA', 'dbo', 'TABLE', 'sys_department', 'COLUMN', 'state'
-go
-
-exec sp_addextendedproperty 'MS_Description', '排序', 'SCHEMA', 'dbo', 'TABLE', 'sys_department', 'COLUMN', 'sort'
-go
-
-exec sp_addextendedproperty 'MS_Description', '备注', 'SCHEMA', 'dbo', 'TABLE', 'sys_department', 'COLUMN', 'remark'
-go
-
-exec sp_addextendedproperty 'MS_Description', '版本', 'SCHEMA', 'dbo', 'TABLE', 'sys_department', 'COLUMN', 'version'
-go
-
-exec sp_addextendedproperty 'MS_Description', '创建时间', 'SCHEMA', 'dbo', 'TABLE', 'sys_department', 'COLUMN', 'create_time'
-go
-
-exec sp_addextendedproperty 'MS_Description', '修改时间', 'SCHEMA', 'dbo', 'TABLE', 'sys_department', 'COLUMN', 'update_time'
-go
-
-create table sys_log
-(
-    log_id      bigint identity
-        constraint sys_log_pk
-        primary key nonclustered,
-    type        smallint,
-    content     varchar(255),
-    create_id   bigint,
-    create_time datetime default getdate() not null
-) go
-
-exec sp_addextendedproperty 'MS_Description', '系统日志', 'SCHEMA', 'dbo', 'TABLE', 'sys_log'
-go
-
-exec sp_addextendedproperty 'MS_Description', '主键', 'SCHEMA', 'dbo', 'TABLE', 'sys_log', 'COLUMN', 'log_id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '类型', 'SCHEMA', 'dbo', 'TABLE', 'sys_log', 'COLUMN', 'type'
-go
-
-exec sp_addextendedproperty 'MS_Description', '内容', 'SCHEMA', 'dbo', 'TABLE', 'sys_log', 'COLUMN', 'content'
-go
-
-exec sp_addextendedproperty 'MS_Description', '创建人ID', 'SCHEMA', 'dbo', 'TABLE', 'sys_log', 'COLUMN', 'create_id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '创建时间', 'SCHEMA', 'dbo', 'TABLE', 'sys_log', 'COLUMN', 'create_time'
-go
-
-create table sys_permission
-(
-    id          bigint identity
-        constraint sys_permission_pk
-        primary key nonclustered,
-    name        varchar(32),
-    parent_id   bigint,
-    url         varchar(200),
-    code        varchar(100)               not null,
-    icon        varchar(100),
-    type        int                        not null,
-    level       int                        not null,
-    state       int      default 1         not null,
-    sort        int      default 0         not null,
-    remark      varchar(200),
-    version     int      default 0         not null,
-    create_time datetime default getdate() not null,
-    update_time datetime
-) go
-
-exec sp_addextendedproperty 'MS_Description', '系统权限', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission'
-go
-
-exec sp_addextendedproperty 'MS_Description', '主键', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '权限名称', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'name'
-go
-
-exec sp_addextendedproperty 'MS_Description', '父id', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'parent_id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '路径', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'url'
-go
-
-exec sp_addextendedproperty 'MS_Description', '唯一编码', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'code'
-go
-
-exec sp_addextendedproperty 'MS_Description', '图标', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'icon'
-go
-
-exec sp_addextendedproperty 'MS_Description', '类型，1：菜单，2：按钮', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'type'
-go
-
-exec sp_addextendedproperty 'MS_Description', '层级，1：第一级，2：第二级，N：第N级', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'level'
-go
-
-exec sp_addextendedproperty 'MS_Description', '状态，0：禁用，1：启用', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'state'
-go
-
-exec sp_addextendedproperty 'MS_Description', '排序', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'sort'
-go
-
-exec sp_addextendedproperty 'MS_Description', '备注', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'remark'
-go
-
-exec sp_addextendedproperty 'MS_Description', '版本', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'version'
-go
-
-exec sp_addextendedproperty 'MS_Description', '创建时间', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'create_time'
-go
-
-exec sp_addextendedproperty 'MS_Description', '修改时间', 'SCHEMA', 'dbo', 'TABLE', 'sys_permission', 'COLUMN', 'update_time'
-go
-
-create table sys_role
-(
-    id          bigint identity
-        constraint sys_role_pk
-        primary key nonclustered,
-    name        varchar(32)                not null,
-    code        varchar(100),
-    type        int,
-    state       int      default 1         not null,
-    remark      varchar(200),
-    version     int      default 0         not null,
-    create_time datetime default getdate() not null,
-    update_time datetime
-) go
-
-exec sp_addextendedproperty 'MS_Description', '系统角色', 'SCHEMA', 'dbo', 'TABLE', 'sys_role'
-go
-
-exec sp_addextendedproperty 'MS_Description', '主键', 'SCHEMA', 'dbo', 'TABLE', 'sys_role', 'COLUMN', 'id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '角色名称', 'SCHEMA', 'dbo', 'TABLE', 'sys_role', 'COLUMN', 'name'
-go
-
-exec sp_addextendedproperty 'MS_Description', '角色唯一编码', 'SCHEMA', 'dbo', 'TABLE', 'sys_role', 'COLUMN', 'code'
-go
-
-exec sp_addextendedproperty 'MS_Description', '角色类型', 'SCHEMA', 'dbo', 'TABLE', 'sys_role', 'COLUMN', 'type'
-go
-
-exec sp_addextendedproperty 'MS_Description', '角色状态，0：禁用，1：启用', 'SCHEMA', 'dbo', 'TABLE', 'sys_role', 'COLUMN', 'state'
-go
-
-exec sp_addextendedproperty 'MS_Description', '备注', 'SCHEMA', 'dbo', 'TABLE', 'sys_role', 'COLUMN', 'remark'
-go
-
-exec sp_addextendedproperty 'MS_Description', '版本', 'SCHEMA', 'dbo', 'TABLE', 'sys_role', 'COLUMN', 'version'
-go
-
-exec sp_addextendedproperty 'MS_Description', '创建时间', 'SCHEMA', 'dbo', 'TABLE', 'sys_role', 'COLUMN', 'create_time'
-go
-
-exec sp_addextendedproperty 'MS_Description', '修改时间', 'SCHEMA', 'dbo', 'TABLE', 'sys_role', 'COLUMN', 'update_time'
-go
-
-create table sys_role_permission
-(
-    id            bigint identity
-        constraint sys_role_permission_pk
-        primary key nonclustered,
-    role_id       bigint                     not null,
-    permission_id bigint                     not null,
-    state         int      default 1         not null,
-    remark        varchar(200),
-    version       int      default 0         not null,
-    create_time   datetime default getdate() not null,
-    update_time   datetime
-) go
-
-exec sp_addextendedproperty 'MS_Description', '角色权限', 'SCHEMA', 'dbo', 'TABLE', 'sys_role_permission'
-go
-
-exec sp_addextendedproperty 'MS_Description', '主键', 'SCHEMA', 'dbo', 'TABLE', 'sys_role_permission', 'COLUMN', 'id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '角色id', 'SCHEMA', 'dbo', 'TABLE', 'sys_role_permission', 'COLUMN', 'role_id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '权限id', 'SCHEMA', 'dbo', 'TABLE', 'sys_role_permission', 'COLUMN', 'permission_id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '状态，0：禁用，1：启用', 'SCHEMA', 'dbo', 'TABLE', 'sys_role_permission', 'COLUMN', 'state'
-go
-
-exec sp_addextendedproperty 'MS_Description', '备注', 'SCHEMA', 'dbo', 'TABLE', 'sys_role_permission', 'COLUMN', 'remark'
-go
-
-exec sp_addextendedproperty 'MS_Description', '版本', 'SCHEMA', 'dbo', 'TABLE', 'sys_role_permission', 'COLUMN', 'version'
-go
-
-exec sp_addextendedproperty 'MS_Description', '创建时间', 'SCHEMA', 'dbo', 'TABLE', 'sys_role_permission', 'COLUMN', 'create_time'
-go
-
-exec sp_addextendedproperty 'MS_Description', '修改时间', 'SCHEMA', 'dbo', 'TABLE', 'sys_role_permission', 'COLUMN', 'update_time'
-go
-
-create table sys_user
-(
-    id            bigint identity
-        constraint sys_user_pk
-        primary key nonclustered,
-    username      varchar(20)                not null,
-    nickname      varchar(20),
-    password      varchar(64)                not null,
-    salt          varchar(32),
-    phone         varchar(20)                not null,
-    gender        int      default 1         not null,
-    head          varchar(200),
-    remark        varchar(200),
-    state         int      default 1         not null,
-    department_id bigint                     not null,
-    role_id       bigint                     not null,
-    deleted       int      default 0         not null,
-    version       int      default 0         not null,
-    create_time   datetime default getdate() not null,
-    update_time   datetime
-) go
-
-exec sp_addextendedproperty 'MS_Description', '系统用户', 'SCHEMA', 'dbo', 'TABLE', 'sys_user'
-go
-
-exec sp_addextendedproperty 'MS_Description', '主键', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '用户名', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'username'
-go
-
-exec sp_addextendedproperty 'MS_Description', '昵称', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'nickname'
-go
-
-exec sp_addextendedproperty 'MS_Description', '密码', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'password'
-go
-
-exec sp_addextendedproperty 'MS_Description', '盐值', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'salt'
-go
-
-exec sp_addextendedproperty 'MS_Description', '手机号码', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'phone'
-go
-
-exec sp_addextendedproperty 'MS_Description', '性别，0：女，1：男，默认1', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'gender'
-go
-
-exec sp_addextendedproperty 'MS_Description', '头像', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'head'
-go
-
-exec sp_addextendedproperty 'MS_Description', '备注', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'remark'
-go
-
-exec sp_addextendedproperty 'MS_Description', '状态，0：禁用，1：启用，2：锁定', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'state'
-go
-
-exec sp_addextendedproperty 'MS_Description', '部门id', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'department_id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '角色id', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'role_id'
-go
-
-exec sp_addextendedproperty 'MS_Description', '逻辑删除，0：未删除，1：已删除', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'deleted'
-go
-
-exec sp_addextendedproperty 'MS_Description', '版本', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'version'
-go
-
-exec sp_addextendedproperty 'MS_Description', '创建时间', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'create_time'
-go
-
-exec sp_addextendedproperty 'MS_Description', '修改时间', 'SCHEMA', 'dbo', 'TABLE', 'sys_user', 'COLUMN', 'update_time'
-go
-
-
-
-set identity_insert spring_boot_plus.dbo.foo_bar on
-INSERT INTO spring_boot_plus.dbo.foo_bar (id, name, foo, bar, remark, state, version, create_time, update_time) VALUES (1, 'FooBar', 'foo', 'bar', 'remark...', 1, 0, '2019-11-01 14:05:14.000', null);
-INSERT INTO spring_boot_plus.dbo.foo_bar (id, name, foo, bar, remark, state, version, create_time, update_time) VALUES (2, 'HelloWorld', 'hello', 'world', null, 1, 0, '2019-11-01 14:05:14.000', null);
-set identity_insert spring_boot_plus.dbo.foo_bar off
-
-
-set identity_insert spring_boot_plus.dbo.sys_department on
-INSERT INTO spring_boot_plus.dbo.sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (1, '管理部', null, 1, 1, 0, null, 0, '2019-10-25 09:46:49.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (2, '技术部', null, 1, 1, 0, null, 0, '2019-11-01 20:45:43.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (20, '前端开发部', 2, 2, 1, 0, null, 0, '2019-11-01 20:48:38.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (21, '后台开发部', 2, 2, 1, 0, null, 0, '2019-11-01 20:48:38.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (22, '测试部', 2, 2, 1, 0, null, 0, '2019-11-01 20:48:38.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (201, '前端一组', 20, 3, 1, 0, null, 0, '2019-11-01 20:48:38.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (202, '前端二组', 20, 3, 1, 0, null, 0, '2019-11-01 20:48:38.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (203, '后台一组', 21, 3, 1, 0, null, 0, '2019-11-01 20:48:38.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (204, '后台二组', 21, 3, 1, 0, null, 0, '2019-11-01 20:48:38.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_department (id, name, parent_id, level, state, sort, remark, version, create_time, update_time) VALUES (205, '测试一组', 22, 3, 1, 0, null, 0, '2019-11-01 20:48:38.000', null);
-set identity_insert spring_boot_plus.dbo.sys_department off
-
-
-set identity_insert spring_boot_plus.dbo.sys_permission on
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1, '系统管理', null, null, 'system:management', null, 1, 1, 1, 0, null, 0, '2019-10-26 11:12:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (100, '用户管理', 1, null, 'sys:user:management', null, 1, 2, 1, 0, null, 0, '2019-10-26 11:15:48.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (200, '角色管理', 1, null, 'sys:role:management', null, 1, 2, 1, 0, null, 0, '2019-10-26 11:15:48.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (300, '权限管理', 1, null, 'sys:permission:management', null, 1, 2, 1, 0, null, 0, '2019-10-26 11:15:48.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (400, '部门管理', 1, null, 'sys:department:management', null, 1, 2, 1, 0, null, 0, '2019-10-26 11:15:48.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1000, '用户新增', 100, null, 'sys:user:add', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1001, '用户修改', 100, null, 'sys:user:update', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1002, '用户删除', 100, null, 'sys:user:delete', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1003, '用户详情', 100, null, 'sys:user:info', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1004, '用户分页列表', 100, null, 'sys:user:page', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1005, '用户修改密码', 100, null, 'sys:user:update:password', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (1006, '用户修改头像', 100, null, 'sys:user:update:head', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (2000, '角色新增', 200, null, 'sys:role:add', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (2001, '角色修改', 200, null, 'sys:role:update', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (2002, '角色删除', 200, null, 'sys:role:delete', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (2003, '角色详情', 200, null, 'sys:role:info', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (2004, '角色分页列表', 200, null, 'sys:role:page', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3000, '权限新增', 300, null, 'sys:permission:add', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3001, '权限修改', 300, null, 'sys:permission:update', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3002, '权限删除', 300, null, 'sys:permission:delete', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3003, '权限详情', 300, null, 'sys:permission:info', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3004, '权限分页列表', 300, null, 'sys:permission:page', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3005, '权限所有列表', 300, null, 'sys:permission:all:menu:list', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3006, '权限所有树形列表', 300, null, 'sys:permission:all:menu:tree', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3007, '权限用户列表', 300, null, 'sys:permission:menu:list', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3008, '权限用户树形列表', 300, null, 'sys:permission:menu:tree', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (3009, '权限用户代码列表', 300, null, 'sys:permission:codes', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (4000, '部门新增', 400, null, 'sys:department:add', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (4001, '部门修改', 400, null, 'sys:department:update', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (4002, '部门删除', 400, null, 'sys:department:delete', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (4003, '部门详情', 400, null, 'sys:department:info', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_permission (id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time) VALUES (4004, '部门分页列表', 400, null, 'sys:department:page', null, 2, 3, 1, 0, null, 0, '2019-10-26 11:18:40.000', null);
-set identity_insert spring_boot_plus.dbo.sys_permission off
-
-
-set identity_insert spring_boot_plus.dbo.sys_role on
-INSERT INTO spring_boot_plus.dbo.sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (1, '管理员', 'admin', null, 1, null, 0, '2019-10-25 09:47:21.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role (id, name, code, type, state, remark, version, create_time, update_time) VALUES (2, 'test', 'test', null, 1, null, 0, '2019-10-25 09:48:02.000', null);
-set identity_insert spring_boot_plus.dbo.sys_role off
-
-
-set identity_insert spring_boot_plus.dbo.sys_role_permission on
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (1, 1, 1, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (2, 1, 100, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (3, 1, 200, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (4, 1, 300, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (5, 1, 400, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (6, 1, 1000, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (7, 1, 1001, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (8, 1, 1002, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (9, 1, 1003, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (10, 1, 1004, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (11, 1, 1005, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (12, 1, 1006, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (13, 1, 2000, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (14, 1, 2001, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (15, 1, 2002, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (16, 1, 2003, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (17, 1, 2004, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (18, 1, 3000, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (19, 1, 3001, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (20, 1, 3002, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (21, 1, 3003, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (22, 1, 3004, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (23, 1, 3005, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (24, 1, 3006, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (25, 1, 3007, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (26, 1, 3008, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (27, 1, 3009, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (28, 1, 4001, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (29, 1, 4002, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (30, 1, 4003, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (31, 1, 4004, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (100, 1, 1, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (101, 1, 100, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (102, 1, 1000, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (103, 1, 1001, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (104, 1, 1002, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (105, 1, 1003, 1, null, 0, '2019-10-26 22:16:19.000', null);
-INSERT INTO spring_boot_plus.dbo.sys_role_permission (id, role_id, permission_id, state, remark, version, create_time, update_time) VALUES (106, 1, 1004, 1, null, 0, '2019-10-26 22:16:19.000', null);
-set identity_insert spring_boot_plus.dbo.sys_role_permission off
-
-
-set identity_insert spring_boot_plus.dbo.sys_user on
-INSERT INTO spring_boot_plus.dbo.sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (1, 'admin', '管理员', '11a254dab80d52bc4a347e030e54d861a9d2cdb2af2185a9ca4a7318e830d04d', '666', '', 1, 'http://localhost:8888//resource/201910281559227.jpg', 'Administrator Account', 1, 1, 1, 0, 1, '2019-08-26 00:52:01.000', '2019-10-27 23:32:29.000');
-INSERT INTO spring_boot_plus.dbo.sys_user (id, username, nickname, password, salt, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time) VALUES (2, 'test', '测试人员', '34783fb724b259beb71a1279f7cd93bdcfd92a273d566f926419a37825c500df', '087c2e9857f35f1e243367f3b89b81c1', '', 1, null, 'Tester Account', 1, 1, 2, 0, 0, '2019-10-05 14:04:27.000', null);
-set identity_insert spring_boot_plus.dbo.sys_user off
\ No newline at end of file
diff --git a/docs/img/geekidea-toutiao.jpeg b/docs/img/geekidea-toutiao.jpeg
deleted file mode 100644
index aebc05c2..00000000
Binary files a/docs/img/geekidea-toutiao.jpeg and /dev/null differ
diff --git a/docs/img/geekidea-wechat-donate.jpeg b/docs/img/geekidea-wechat-donate.jpeg
deleted file mode 100644
index cc0a8ff6..00000000
Binary files a/docs/img/geekidea-wechat-donate.jpeg and /dev/null differ
diff --git a/docs/img/geekidea-wechat-official.jpg b/docs/img/geekidea-wechat-official.jpg
deleted file mode 100644
index 620c4774..00000000
Binary files a/docs/img/geekidea-wechat-official.jpg and /dev/null differ
diff --git a/docs/img/spring-boot-plus-maven-diagrams.png b/docs/img/spring-boot-plus-maven-diagrams.png
deleted file mode 100644
index 42f01840..00000000
Binary files a/docs/img/spring-boot-plus-maven-diagrams.png and /dev/null differ
diff --git a/docs/img/spring-boot-plus-qq-group.png b/docs/img/spring-boot-plus-qq-group.png
deleted file mode 100644
index 5bfc6c42..00000000
Binary files a/docs/img/spring-boot-plus-qq-group.png and /dev/null differ
diff --git a/example/.gitignore b/example/.gitignore
deleted file mode 100644
index ccdf702f..00000000
--- a/example/.gitignore
+++ /dev/null
@@ -1,36 +0,0 @@
-/target/
-/classes
-!.mvn/wrapper/maven-wrapper.jar
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-
-### NetBeans ###
-/nbproject/private/
-/build/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-
-.DS_Store
-
-*.log
-logs
-
-*.rdb
-
-
-
diff --git a/example/README.md b/example/README.md
deleted file mode 100644
index ae078738..00000000
--- a/example/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# example 项目示例模块
\ No newline at end of file
diff --git a/example/pom.xml b/example/pom.xml
deleted file mode 100644
index afed3bd5..00000000
--- a/example/pom.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>io.geekidea.springbootplus</groupId>
-        <artifactId>parent</artifactId>
-        <version>2.0</version>
-    </parent>
-
-    <artifactId>example</artifactId>
-    <name>example</name>
-    <description>业务自定义模块，可新建N个业务模块，在bootstrap模块中引入自定义模块</description>
-
-    <dependencies>
-        <dependency>
-            <groupId>io.geekidea.springbootplus</groupId>
-            <artifactId>framework</artifactId>
-        </dependency>
-    </dependencies>
-
-</project>
diff --git a/example/src/main/java/com/example/foobar/controller/FooBarController.java b/example/src/main/java/com/example/foobar/controller/FooBarController.java
deleted file mode 100644
index a5e98a8c..00000000
--- a/example/src/main/java/com/example/foobar/controller/FooBarController.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.foobar.controller;
-
-import com.example.foobar.entity.FooBar;
-import com.example.foobar.param.FooBarPageParam;
-import com.example.foobar.service.FooBarService;
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.common.controller.BaseController;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.core.validator.groups.Add;
-import io.geekidea.springbootplus.framework.core.validator.groups.Update;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-/**
- * FooBar 控制器
- *
- * @author geekidea
- * @since 2020-03-24
- */
-@Slf4j
-@RestController
-@RequestMapping("/fooBar")
-@Module("foobar")
-@Api(value = "FooBarAPI", tags = {"FooBar"})
-public class FooBarController extends BaseController {
-
-    @Autowired
-    private FooBarService fooBarService;
-
-    /**
-     * 添加FooBar
-     */
-    @PostMapping("/add")
-    @OperationLog(name = "添加FooBar", type = OperationLogType.ADD)
-    @ApiOperation(value = "添加FooBar", response = ApiResult.class)
-    public ApiResult<Boolean> addFooBar(@Validated(Add.class) @RequestBody FooBar fooBar) throws Exception {
-        boolean flag = fooBarService.saveFooBar(fooBar);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 修改FooBar
-     */
-    @PostMapping("/update")
-    @OperationLog(name = "修改FooBar", type = OperationLogType.UPDATE)
-    @ApiOperation(value = "修改FooBar", response = ApiResult.class)
-    public ApiResult<Boolean> updateFooBar(@Validated(Update.class) @RequestBody FooBar fooBar) throws Exception {
-        boolean flag = fooBarService.updateFooBar(fooBar);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 删除FooBar
-     */
-    @PostMapping("/delete/{id}")
-    @OperationLog(name = "删除FooBar", type = OperationLogType.DELETE)
-    @ApiOperation(value = "删除FooBar", response = ApiResult.class)
-    public ApiResult<Boolean> deleteFooBar(@PathVariable("id") Long id) throws Exception {
-        boolean flag = fooBarService.deleteFooBar(id);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 获取FooBar详情
-     */
-    @GetMapping("/info/{id}")
-    @OperationLog(name = "FooBar详情", type = OperationLogType.INFO)
-    @ApiOperation(value = "FooBar详情", response = FooBar.class)
-    public ApiResult<FooBar> getFooBar(@PathVariable("id") Long id) throws Exception {
-        FooBar fooBar = fooBarService.getById(id);
-        return ApiResult.ok(fooBar);
-    }
-
-    /**
-     * FooBar分页列表
-     */
-    @PostMapping("/getPageList")
-    @OperationLog(name = "FooBar分页列表", type = OperationLogType.PAGE)
-    @ApiOperation(value = "FooBar分页列表", response = FooBar.class)
-    public ApiResult<Paging<FooBar>> getFooBarPageList(@Validated @RequestBody FooBarPageParam fooBarPageParam) throws Exception {
-        Paging<FooBar> paging = fooBarService.getFooBarPageList(fooBarPageParam);
-        return ApiResult.ok(paging);
-    }
-
-}
-
diff --git a/example/src/main/java/com/example/foobar/entity/FooBar.java b/example/src/main/java/com/example/foobar/entity/FooBar.java
deleted file mode 100644
index 42df4c65..00000000
--- a/example/src/main/java/com/example/foobar/entity/FooBar.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.foobar.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.Version;
-import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
-import io.geekidea.springbootplus.framework.core.validator.groups.Update;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import java.util.Date;
-
-/**
- * FooBar
- *
- * @author geekidea
- * @since 2020-03-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "FooBar对象")
-public class FooBar extends BaseEntity {
-    private static final long serialVersionUID = 1L;
-
-    @NotNull(message = "id不能为空", groups = {Update.class})
-    @ApiModelProperty("ID")
-    @TableId(value = "id", type = IdType.AUTO)
-    private Long id;
-
-    @NotBlank(message = "Name不能为空")
-    @ApiModelProperty("Name")
-    private String name;
-
-    @ApiModelProperty("Foo")
-    private String foo;
-
-    @NotBlank(message = "Bar不能为空")
-    @ApiModelProperty("Bar")
-    private String bar;
-
-    @ApiModelProperty("Remark")
-    private String remark;
-
-    @ApiModelProperty("State，0：Disable，1：Enable")
-    private Integer state;
-
-    @ApiModelProperty("Version")
-    @Version
-    private Integer version;
-
-    @ApiModelProperty("Create Time")
-    private Date createTime;
-
-    @ApiModelProperty("Update Time")
-    private Date updateTime;
-
-}
diff --git a/example/src/main/java/com/example/foobar/mapper/FooBarMapper.java b/example/src/main/java/com/example/foobar/mapper/FooBarMapper.java
deleted file mode 100644
index 9e26e2cc..00000000
--- a/example/src/main/java/com/example/foobar/mapper/FooBarMapper.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.foobar.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.example.foobar.entity.FooBar;
-import org.springframework.stereotype.Repository;
-
-/**
- * FooBar Mapper 接口
- *
- * @author geekidea
- * @since 2020-03-24
- */
-@Repository
-public interface FooBarMapper extends BaseMapper<FooBar> {
-
-
-}
diff --git a/example/src/main/java/com/example/foobar/param/FooBarPageParam.java b/example/src/main/java/com/example/foobar/param/FooBarPageParam.java
deleted file mode 100644
index 8baca649..00000000
--- a/example/src/main/java/com/example/foobar/param/FooBarPageParam.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.foobar.param;
-
-import io.geekidea.springbootplus.framework.core.pagination.BasePageOrderParam;
-import io.swagger.annotations.ApiModel;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-/**
- * <pre>
- * FooBar 分页参数对象
- * </pre>
- *
- * @author geekidea
- * @date 2020-03-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "FooBar分页参数")
-public class FooBarPageParam extends BasePageOrderParam {
-    private static final long serialVersionUID = 1L;
-}
diff --git a/example/src/main/java/com/example/foobar/service/FooBarService.java b/example/src/main/java/com/example/foobar/service/FooBarService.java
deleted file mode 100644
index 1f9d0857..00000000
--- a/example/src/main/java/com/example/foobar/service/FooBarService.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.foobar.service;
-
-import com.example.foobar.entity.FooBar;
-import com.example.foobar.param.FooBarPageParam;
-import io.geekidea.springbootplus.framework.common.service.BaseService;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-
-/**
- * FooBar 服务类
- *
- * @author geekidea
- * @since 2020-03-24
- */
-public interface FooBarService extends BaseService<FooBar> {
-
-    /**
-     * 保存
-     *
-     * @param fooBar
-     * @return
-     * @throws Exception
-     */
-    boolean saveFooBar(FooBar fooBar) throws Exception;
-
-    /**
-     * 修改
-     *
-     * @param fooBar
-     * @return
-     * @throws Exception
-     */
-    boolean updateFooBar(FooBar fooBar) throws Exception;
-
-    /**
-     * 删除
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    boolean deleteFooBar(Long id) throws Exception;
-
-
-    /**
-     * 获取分页对象
-     *
-     * @param fooBarQueryParam
-     * @return
-     * @throws Exception
-     */
-    Paging<FooBar> getFooBarPageList(FooBarPageParam fooBarPageParam) throws Exception;
-
-}
diff --git a/example/src/main/java/com/example/foobar/service/impl/FooBarServiceImpl.java b/example/src/main/java/com/example/foobar/service/impl/FooBarServiceImpl.java
deleted file mode 100644
index 5488a122..00000000
--- a/example/src/main/java/com/example/foobar/service/impl/FooBarServiceImpl.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.foobar.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.example.foobar.entity.FooBar;
-import com.example.foobar.mapper.FooBarMapper;
-import com.example.foobar.param.FooBarPageParam;
-import com.example.foobar.service.FooBarService;
-import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
-import io.geekidea.springbootplus.framework.core.pagination.OrderMapping;
-import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-/**
- * FooBar 服务实现类
- *
- * @author geekidea
- * @since 2020-03-24
- */
-@Slf4j
-@Service
-public class FooBarServiceImpl extends BaseServiceImpl<FooBarMapper, FooBar> implements FooBarService {
-
-    @Autowired
-    private FooBarMapper fooBarMapper;
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean saveFooBar(FooBar fooBar) throws Exception {
-        return super.save(fooBar);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean updateFooBar(FooBar fooBar) throws Exception {
-        return super.updateById(fooBar);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean deleteFooBar(Long id) throws Exception {
-        return super.removeById(id);
-    }
-
-    @Override
-    public Paging<FooBar> getFooBarPageList(FooBarPageParam fooBarPageParam) throws Exception {
-        OrderMapping orderMapping = new OrderMapping()
-                .mapping("updateTime", "update_time");
-        Page<FooBar> page = new PageInfo<>(fooBarPageParam, OrderItem.desc(getLambdaColumn(FooBar::getCreateTime)), orderMapping);
-        LambdaQueryWrapper<FooBar> wrapper = new LambdaQueryWrapper<>();
-        IPage<FooBar> iPage = fooBarMapper.selectPage(page, wrapper);
-        return new Paging<FooBar>(iPage);
-    }
-
-}
diff --git a/example/src/main/java/com/example/foobar/vo/FooBarQueryVo.java b/example/src/main/java/com/example/foobar/vo/FooBarQueryVo.java
deleted file mode 100644
index 078f68cf..00000000
--- a/example/src/main/java/com/example/foobar/vo/FooBarQueryVo.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.foobar.vo;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Date;
-
-/**
- * <pre>
- * FooBar 查询结果对象
- * </pre>
- *
- * @author geekidea
- * @date 2020-03-23
- */
-@Data
-@Accessors(chain = true)
-@ApiModel(value = "FooBarQueryVo对象")
-public class FooBarQueryVo implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty("ID")
-    private Long id;
-
-    @ApiModelProperty("Name")
-    private String name;
-
-    @ApiModelProperty("Foo")
-    private String foo;
-
-    @ApiModelProperty("Bar")
-    private String bar;
-
-    @ApiModelProperty("Remark")
-    private String remark;
-
-    @ApiModelProperty("State，0：Disable，1：Enable")
-    private Integer state;
-
-    @ApiModelProperty("Version")
-    private Integer version;
-
-    @ApiModelProperty("Create Time")
-    private Date createTime;
-
-    @ApiModelProperty("Update Time")
-    private Date updateTime;
-
-}
\ No newline at end of file
diff --git a/example/src/main/java/com/example/order/controller/ExampleOrderController.java b/example/src/main/java/com/example/order/controller/ExampleOrderController.java
deleted file mode 100644
index f5de7e02..00000000
--- a/example/src/main/java/com/example/order/controller/ExampleOrderController.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.order.controller;
-
-import com.example.order.entity.ExampleOrder;
-import com.example.order.service.ExampleOrderService;
-import lombok.extern.slf4j.Slf4j;
-import com.example.order.param.ExampleOrderPageParam;
-import io.geekidea.springbootplus.framework.common.controller.BaseController;
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.common.param.IdParam;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-import io.geekidea.springbootplus.framework.core.validator.groups.Add;
-import io.geekidea.springbootplus.framework.core.validator.groups.Update;
-import org.springframework.validation.annotation.Validated;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-/**
- * 订单示例 控制器
- *
- * @author geekidea
- * @since 2020-03-27
- */
-@Slf4j
-@RestController
-@RequestMapping("/exampleOrder")
-@Module("order")
-@Api(value = "ExampleOrderAPI", tags = {"ExampleOrder"})
-public class ExampleOrderController extends BaseController {
-
-    @Autowired
-    private ExampleOrderService exampleOrderService;
-
-    /**
-     * 添加订单示例
-     */
-    @PostMapping("/add")
-    @OperationLog(name = "添加订单示例", type = OperationLogType.ADD)
-    @ApiOperation(value = "添加订单示例", response = ApiResult.class)
-    public ApiResult<Boolean> addExampleOrder(@Validated(Add.class) @RequestBody ExampleOrder exampleOrder) throws Exception {
-        boolean flag = exampleOrderService.saveExampleOrder(exampleOrder);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 修改订单示例
-     */
-    @PostMapping("/update")
-    @OperationLog(name = "修改订单示例", type = OperationLogType.UPDATE)
-    @ApiOperation(value = "修改订单示例", response = ApiResult.class)
-    public ApiResult<Boolean> updateExampleOrder(@Validated(Update.class) @RequestBody ExampleOrder exampleOrder) throws Exception {
-        boolean flag = exampleOrderService.updateExampleOrder(exampleOrder);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 删除订单示例
-     */
-    @PostMapping("/delete/{id}")
-    @OperationLog(name = "删除订单示例", type = OperationLogType.DELETE)
-    @ApiOperation(value = "删除订单示例", response = ApiResult.class)
-    public ApiResult<Boolean> deleteExampleOrder(@PathVariable("id") Long id) throws Exception {
-        boolean flag = exampleOrderService.deleteExampleOrder(id);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 获取订单示例详情
-     */
-    @GetMapping("/info/{id}")
-    @OperationLog(name = "订单示例详情", type = OperationLogType.INFO)
-    @ApiOperation(value = "订单示例详情", response = ExampleOrder.class)
-    public ApiResult<ExampleOrder> getExampleOrder(@PathVariable("id") Long id) throws Exception {
-        ExampleOrder exampleOrder = exampleOrderService.getById(id);
-        return ApiResult.ok(exampleOrder);
-    }
-
-    /**
-     * 订单示例分页列表
-     */
-    @PostMapping("/getPageList")
-    @OperationLog(name = "订单示例分页列表", type = OperationLogType.PAGE)
-    @ApiOperation(value = "订单示例分页列表", response = ExampleOrder.class)
-    public ApiResult<Paging<ExampleOrder>> getExampleOrderPageList(@Validated @RequestBody ExampleOrderPageParam exampleOrderPageParam) throws Exception {
-        Paging<ExampleOrder> paging = exampleOrderService.getExampleOrderPageList(exampleOrderPageParam);
-        return ApiResult.ok(paging);
-    }
-
-}
-
diff --git a/example/src/main/java/com/example/order/entity/ExampleOrder.java b/example/src/main/java/com/example/order/entity/ExampleOrder.java
deleted file mode 100644
index e7a0290b..00000000
--- a/example/src/main/java/com/example/order/entity/ExampleOrder.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.order.entity;
-
-import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
-import com.baomidou.mybatisplus.annotation.IdType;
-import java.util.Date;
-import com.baomidou.mybatisplus.annotation.Version;
-import com.baomidou.mybatisplus.annotation.TableId;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import io.geekidea.springbootplus.framework.core.validator.groups.Update;
-
-/**
- * 订单示例
- *
- * @author geekidea
- * @since 2020-03-27
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "ExampleOrder对象")
-public class ExampleOrder extends BaseEntity {
-    private static final long serialVersionUID = 1L;
-
-    @NotNull(message = "id不能为空", groups = {Update.class})
-    @ApiModelProperty("主键")
-    @TableId(value = "id", type = IdType.AUTO)
-    private Long id;
-
-    @NotBlank(message = "订单名称不能为空")
-    @ApiModelProperty("订单名称")
-    private String name;
-
-    @ApiModelProperty("订单编号")
-    private String orderNo;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("状态，0：禁用，1：启用")
-    private Integer state;
-
-    @ApiModelProperty("版本")
-    @Version
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    private Date updateTime;
-
-}
diff --git a/example/src/main/java/com/example/order/mapper/ExampleOrderMapper.java b/example/src/main/java/com/example/order/mapper/ExampleOrderMapper.java
deleted file mode 100644
index 3e677f41..00000000
--- a/example/src/main/java/com/example/order/mapper/ExampleOrderMapper.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.order.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.example.order.entity.ExampleOrder;
-import com.example.order.param.ExampleOrderPageParam;
-
-import org.springframework.stereotype.Repository;
-
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import org.apache.ibatis.annotations.Param;
-import java.io.Serializable;
-
-/**
- * 订单示例 Mapper 接口
- *
- * @author geekidea
- * @since 2020-03-27
- */
-@Repository
-public interface ExampleOrderMapper extends BaseMapper<ExampleOrder> {
-
-
-}
diff --git a/example/src/main/java/com/example/order/param/ExampleOrderPageParam.java b/example/src/main/java/com/example/order/param/ExampleOrderPageParam.java
deleted file mode 100644
index 6b25f18d..00000000
--- a/example/src/main/java/com/example/order/param/ExampleOrderPageParam.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.order.param;
-
-import io.geekidea.springbootplus.framework.core.pagination.BasePageOrderParam;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-/**
- * <pre>
- * 订单示例 分页参数对象
- * </pre>
- *
- * @author geekidea
- * @date 2020-03-27
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "订单示例分页参数")
-public class ExampleOrderPageParam extends BasePageOrderParam {
-    private static final long serialVersionUID = 6092080418269664419L;
-
-    @ApiModelProperty("订单名称")
-    private String name;
-
-    @ApiModelProperty("订单编号")
-    private String orderNo;
-
-}
diff --git a/example/src/main/java/com/example/order/service/ExampleOrderService.java b/example/src/main/java/com/example/order/service/ExampleOrderService.java
deleted file mode 100644
index 3246ca71..00000000
--- a/example/src/main/java/com/example/order/service/ExampleOrderService.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.order.service;
-
-import com.example.order.entity.ExampleOrder;
-import com.example.order.param.ExampleOrderPageParam;
-import io.geekidea.springbootplus.framework.common.service.BaseService;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-
-/**
- * 订单示例 服务类
- *
- * @author geekidea
- * @since 2020-03-27
- */
-public interface ExampleOrderService extends BaseService<ExampleOrder> {
-
-    /**
-     * 保存
-     *
-     * @param exampleOrder
-     * @return
-     * @throws Exception
-     */
-    boolean saveExampleOrder(ExampleOrder exampleOrder) throws Exception;
-
-    /**
-     * 修改
-     *
-     * @param exampleOrder
-     * @return
-     * @throws Exception
-     */
-    boolean updateExampleOrder(ExampleOrder exampleOrder) throws Exception;
-
-    /**
-     * 删除
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    boolean deleteExampleOrder(Long id) throws Exception;
-
-
-    /**
-     * 获取分页对象
-     *
-     * @param exampleOrderQueryParam
-     * @return
-     * @throws Exception
-     */
-    Paging<ExampleOrder> getExampleOrderPageList(ExampleOrderPageParam exampleOrderPageParam) throws Exception;
-
-}
diff --git a/example/src/main/java/com/example/order/service/impl/ExampleOrderServiceImpl.java b/example/src/main/java/com/example/order/service/impl/ExampleOrderServiceImpl.java
deleted file mode 100644
index f3b0880f..00000000
--- a/example/src/main/java/com/example/order/service/impl/ExampleOrderServiceImpl.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package com.example.order.service.impl;
-
-import com.example.order.entity.ExampleOrder;
-import com.example.order.mapper.ExampleOrderMapper;
-import com.example.order.service.ExampleOrderService;
-import com.example.order.param.ExampleOrderPageParam;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.transaction.annotation.Transactional;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.beans.factory.annotation.Autowired;
-
-/**
- * 订单示例 服务实现类
- *
- * @author geekidea
- * @since 2020-03-27
- */
-@Slf4j
-@Service
-public class ExampleOrderServiceImpl extends BaseServiceImpl<ExampleOrderMapper, ExampleOrder> implements ExampleOrderService {
-
-    @Autowired
-    private ExampleOrderMapper exampleOrderMapper;
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean saveExampleOrder(ExampleOrder exampleOrder) throws Exception {
-        return super.save(exampleOrder);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean updateExampleOrder(ExampleOrder exampleOrder) throws Exception {
-        return super.updateById(exampleOrder);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean deleteExampleOrder(Long id) throws Exception {
-        return super.removeById(id);
-    }
-
-    @Override
-    public Paging<ExampleOrder> getExampleOrderPageList(ExampleOrderPageParam exampleOrderPageParam) throws Exception {
-        Page<ExampleOrder> page = new PageInfo<>(exampleOrderPageParam, OrderItem.desc(getLambdaColumn(ExampleOrder::getCreateTime)));
-        LambdaQueryWrapper<ExampleOrder> wrapper = new LambdaQueryWrapper<>();
-        String keyword = exampleOrderPageParam.getKeyword();
-        String name = exampleOrderPageParam.getName();
-        String orderNo = exampleOrderPageParam.getOrderNo();
-        // keyword模糊查询
-        if (StringUtils.isNotBlank(keyword)) {
-            wrapper.like(ExampleOrder::getName, keyword).or().like(ExampleOrder::getOrderNo, keyword);
-        }
-        // name模糊查询
-        if (StringUtils.isNotBlank(name)) {
-            wrapper.like(ExampleOrder::getName, name);
-        }
-        // 订单号模糊查询
-        if (StringUtils.isNotBlank(orderNo)) {
-            wrapper.like(ExampleOrder::getOrderNo, orderNo);
-        }
-        IPage<ExampleOrder> iPage = exampleOrderMapper.selectPage(page, wrapper);
-        return new Paging<ExampleOrder>(iPage);
-    }
-
-}
diff --git a/example/src/main/resources/mapper/foobar/FooBarMapper.xml b/example/src/main/resources/mapper/foobar/FooBarMapper.xml
deleted file mode 100644
index 8fa137f9..00000000
--- a/example/src/main/resources/mapper/foobar/FooBarMapper.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.example.foobar.mapper.FooBarMapper">
-
-</mapper>
diff --git a/example/src/main/resources/mapper/order/ExampleOrderMapper.xml b/example/src/main/resources/mapper/order/ExampleOrderMapper.xml
deleted file mode 100644
index 4c732181..00000000
--- a/example/src/main/resources/mapper/order/ExampleOrderMapper.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="com.example.order.mapper.ExampleOrderMapper">
-
-</mapper>
diff --git a/framework/.gitignore b/framework/.gitignore
deleted file mode 100644
index ccdf702f..00000000
--- a/framework/.gitignore
+++ /dev/null
@@ -1,36 +0,0 @@
-/target/
-/classes
-!.mvn/wrapper/maven-wrapper.jar
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-
-### NetBeans ###
-/nbproject/private/
-/build/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-
-.DS_Store
-
-*.log
-logs
-
-*.rdb
-
-
-
diff --git a/framework/README.md b/framework/README.md
deleted file mode 100644
index 0859a1d8..00000000
--- a/framework/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# framework 项目核心框架模块
\ No newline at end of file
diff --git a/framework/pom.xml b/framework/pom.xml
deleted file mode 100644
index 7412c99e..00000000
--- a/framework/pom.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>io.geekidea.springbootplus</groupId>
-        <artifactId>parent</artifactId>
-        <version>2.0</version>
-    </parent>
-
-    <artifactId>framework</artifactId>
-    <name>framework</name>
-    <description>spring-boot-plus脚手架核心模块</description>
-
-    <dependencies>
-        <!-- spring-boot start -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-aop</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-tomcat</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-jdbc</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-data-redis</artifactId>
-        </dependency>
-        <!-- spring-boot end -->
-
-        <!-- mybatis-plus begin -->
-        <dependency>
-            <groupId>com.baomidou</groupId>
-            <artifactId>mybatis-plus-boot-starter</artifactId>
-        </dependency>
-        <!-- mybatis-plus end -->
-
-        <!-- MySQL -->
-        <dependency>
-            <groupId>mysql</groupId>
-            <artifactId>mysql-connector-java</artifactId>
-        </dependency>
-
-        <!-- swagger start -->
-        <dependency>
-            <groupId>io.springfox</groupId>
-            <artifactId>springfox-swagger2</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>io.springfox</groupId>
-            <artifactId>springfox-swagger-ui</artifactId>
-        </dependency>
-        <!-- swagger end -->
-        <dependency>
-            <groupId>com.github.xiaoymin</groupId>
-            <artifactId>knife4j-spring-boot-starter</artifactId>
-        </dependency>
-
-        <!-- Shiro+JWT start -->
-        <dependency>
-            <groupId>org.apache.shiro</groupId>
-            <artifactId>shiro-spring</artifactId>
-            <version>${shiro.version}</version>
-        </dependency>
-
-        <dependency>
-            <groupId>com.auth0</groupId>
-            <artifactId>java-jwt</artifactId>
-            <version>${jwt.version}</version>
-        </dependency>
-        <!-- Shiro+JWT end -->
-
-        <dependency>
-            <groupId>io.geekidea.springbootplus</groupId>
-            <artifactId>config</artifactId>
-        </dependency>
-
-    </dependencies>
-
-</project>
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/api/ApiCode.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/api/ApiCode.java
deleted file mode 100644
index 1e0607f5..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/api/ApiCode.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.api;
-
-/**
- * <p>
- * REST API 响应码
- * </p>
- *
- * @author geekidea
- * @since 2018-11-08
- */
-public enum ApiCode {
-
-    /**
-     * 操作成功
-     **/
-    SUCCESS(200, "操作成功"),
-    /**
-     * 非法访问
-     **/
-    UNAUTHORIZED(401, "非法访问"),
-    /**
-     * 没有权限
-     **/
-    NOT_PERMISSION(403, "没有权限"),
-    /**
-     * 你请求的资源不存在
-     **/
-    NOT_FOUND(404, "你请求的资源不存在"),
-    /**
-     * 操作失败
-     **/
-    FAIL(500, "操作失败"),
-    /**
-     * 登录失败
-     **/
-    LOGIN_EXCEPTION(4000, "登录失败"),
-    /**
-     * 系统异常
-     **/
-    SYSTEM_EXCEPTION(5000, "系统异常"),
-    /**
-     * 请求参数校验异常
-     **/
-    PARAMETER_EXCEPTION(5001, "请求参数校验异常"),
-    /**
-     * 请求参数解析异常
-     **/
-    PARAMETER_PARSE_EXCEPTION(5002, "请求参数解析异常"),
-    /**
-     * HTTP内容类型异常
-     **/
-    HTTP_MEDIA_TYPE_EXCEPTION(5003, "HTTP内容类型异常"),
-    /**
-     * 系统处理异常
-     **/
-    SPRING_BOOT_PLUS_EXCEPTION(5100, "系统处理异常"),
-    /**
-     * 业务处理异常
-     **/
-    BUSINESS_EXCEPTION(5101, "业务处理异常"),
-    /**
-     * 数据库处理异常
-     **/
-    DAO_EXCEPTION(5102, "数据库处理异常"),
-    /**
-     * 验证码校验异常
-     **/
-    VERIFICATION_CODE_EXCEPTION(5103, "验证码校验异常"),
-    /**
-     * 登录授权异常
-     **/
-    AUTHENTICATION_EXCEPTION(5104, "登录授权异常"),
-    /**
-     * 没有访问权限
-     **/
-    UNAUTHENTICATED_EXCEPTION(5105, "没有访问权限"),
-    /**
-     * 没有访问权限
-     **/
-    UNAUTHORIZED_EXCEPTION(5106, "没有访问权限"),
-    /**
-     * JWT Token解析异常
-     **/
-    JWTDECODE_EXCEPTION(5107, "Token解析异常"),
-
-    HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION(5108, "METHOD NOT SUPPORTED"),
-
-    ;
-
-    private final int code;
-    private final String message;
-
-    ApiCode(final int code, final String message) {
-        this.code = code;
-        this.message = message;
-    }
-
-    public static ApiCode getApiCode(int code) {
-        ApiCode[] ecs = ApiCode.values();
-        for (ApiCode ec : ecs) {
-            if (ec.getCode() == code) {
-                return ec;
-            }
-        }
-        return SUCCESS;
-    }
-
-    public int getCode() {
-        return code;
-    }
-
-    public String getMessage() {
-        return message;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/api/ApiResult.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/api/ApiResult.java
deleted file mode 100644
index e076b8e8..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/api/ApiResult.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.api;
-
-import com.alibaba.fastjson.annotation.JSONField;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.apache.commons.lang3.StringUtils;
-
-import java.io.Serializable;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * <p>
- * REST API 返回结果
- * </p>
- *
- * @author geekidea
- * @since 2018-11-08
- */
-@Data
-@Accessors(chain = true)
-@Builder
-@AllArgsConstructor
-public class ApiResult<T> implements Serializable {
-	private static final long serialVersionUID = 8004487252556526569L;
-
-	/**
-     * 响应码
-     */
-    private int code;
-
-    /**
-     * 是否成功
-     */
-    private boolean success;
-
-    /**
-     * 响应消息
-     */
-    private String message;
-
-    /**
-     * 响应数据
-     */
-    private T data;
-
-    /**
-     * 响应时间
-     */
-    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    private Date time;
-
-    public ApiResult() {
-        time  = new Date();
-    }
-
-    public static ApiResult<Boolean> result(boolean flag){
-        if (flag){
-            return ok();
-        }
-        return fail();
-    }
-
-    public static ApiResult<Boolean> result(ApiCode apiCode){
-        return result(apiCode,null);
-    }
-
-    public static <T> ApiResult<T> result(ApiCode apiCode,T data){
-        return result(apiCode,null,data);
-    }
-
-    public static <T> ApiResult<T> result(ApiCode apiCode,String message,T data){
-        boolean success = false;
-        if (apiCode.getCode() == ApiCode.SUCCESS.getCode()){
-            success = true;
-        }
-        String apiMessage = apiCode.getMessage();
-        if (StringUtils.isNotBlank(apiMessage)){
-            message = apiMessage;
-        }
-        return (ApiResult<T>) ApiResult.builder()
-                .code(apiCode.getCode())
-                .message(message)
-                .data(data)
-                .success(success)
-                .time(new Date())
-                .build();
-    }
-
-    public static ApiResult<Boolean> ok(){
-        return ok(null);
-    }
-
-    public static <T> ApiResult<T> ok(T data){
-        return result(ApiCode.SUCCESS,data);
-    }
-
-    public static <T> ApiResult<T> ok(T data,String message){
-        return result(ApiCode.SUCCESS,message,data);
-    }
-
-    public static ApiResult<Map<String,Object>> okMap(String key,Object value){
-        Map<String,Object> map = new HashMap<>(1);
-        map.put(key,value);
-        return ok(map);
-    }
-
-    public static ApiResult<Boolean> fail(ApiCode apiCode){
-        return result(apiCode,null);
-    }
-
-    public static ApiResult<String> fail(String message){
-        return result(ApiCode.FAIL,message,null);
-
-    }
-
-    public static <T> ApiResult<T> fail(ApiCode apiCode,T data){
-        if (ApiCode.SUCCESS == apiCode){
-            throw new RuntimeException("失败结果状态码不能为" + ApiCode.SUCCESS.getCode());
-        }
-        return result(apiCode,data);
-
-    }
-
-    public static  ApiResult<String> fail(Integer errorCode,String message){
-        return new ApiResult<String>()
-                .setSuccess(false)
-                .setCode(errorCode)
-                .setMessage(message);
-    }
-
-    public static ApiResult<Map<String,Object>> fail(String key,Object value){
-        Map<String,Object> map = new HashMap<>(1);
-        map.put(key,value);
-        return result(ApiCode.FAIL,map);
-    }
-
-    public static ApiResult<Boolean> fail() {
-        return fail(ApiCode.FAIL);
-    }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/bean/ClientInfo.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/bean/ClientInfo.java
deleted file mode 100644
index 6cafa0eb..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/bean/ClientInfo.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.bean;
-
-import lombok.Data;
-
-import java.io.Serializable;
-
-/**
- * <p>
- * 用户客户端信息对象
- * </p>
- *
- * @author geekidea
- * @date 2019-05-23
- **/
-@Data
-public class ClientInfo implements Serializable {
-
-    private static final long serialVersionUID = -5549531244606897514L;
-
-    /**
-     * ip
-     */
-    private String ip;
-
-    /**
-     * ip对应的地址
-     */
-    private String addree;
-
-    /**
-     * 浏览器名称
-     */
-    private String browserName;
-
-    /**
-     * 浏览器版本
-     */
-    private String browserversion;
-
-    /**
-     * 浏览器引擎名称
-     */
-    private String engineName;
-
-    /**
-     * 浏览器引擎版本
-     */
-    private String engineVersion;
-
-    /**
-     * 系统名称
-     */
-    private String osName;
-
-    /**
-     * 平台名称
-     */
-    private String platformName;
-
-    /**
-     * 是否是手机
-     */
-    private boolean mobile;
-
-    /**
-     * 移动端设备型号
-     */
-    private String deviceName;
-
-    /**
-     * 移动端设备型号
-     */
-    private String deviceModel;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/bean/DeviceInfo.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/bean/DeviceInfo.java
deleted file mode 100644
index cb087889..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/bean/DeviceInfo.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.bean;
-
-import lombok.Data;
-
-import java.io.Serializable;
-
-/**
- * <p>
- * 设备信息
- * </p>
- *
- * @author geekidea
- * @date 2019-05-24
- **/
-@Data
-public class DeviceInfo implements Serializable {
-    private static final long serialVersionUID = -5912785220335057555L;
-
-    /**
-     * 设备名称
-     */
-    private String name;
-
-    /**
-     * 设备型号
-     */
-    private String model;
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/controller/BaseController.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/controller/BaseController.java
deleted file mode 100644
index a37217e2..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/controller/BaseController.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.controller;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * Controller父类
- *
- * @author geekidea
- * @date 2018-11-08
- */
-@Slf4j
-public abstract class BaseController {
-
-}
\ No newline at end of file
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/controller/CsrfController.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/controller/CsrfController.java
deleted file mode 100644
index 0cc4f668..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/controller/CsrfController.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.controller;
-
-import io.geekidea.springbootplus.framework.log.annotation.OperationLogIgnore;
-import io.geekidea.springbootplus.framework.util.UUIDUtil;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-import springfox.documentation.annotations.ApiIgnore;
-
-/**
- * CSRF 供swagger调用
- *
- * @author geekidea
- * @date 2019/12/10
- **/
-@ApiIgnore
-@OperationLogIgnore
-@RestController
-public class CsrfController {
-
-    @RequestMapping(value = "/csrf", method = {RequestMethod.GET, RequestMethod.POST})
-    public String csrf() {
-        return UUIDUtil.getUuid();
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/controller/EnumController.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/controller/EnumController.java
deleted file mode 100644
index 5e279eda..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/controller/EnumController.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.controller;
-
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.common.enums.BaseEnum;
-import io.geekidea.springbootplus.framework.common.vo.EnumVo;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLogIgnore;
-import io.geekidea.springbootplus.framework.util.BaseEnumUtil;
-import io.swagger.annotations.Api;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.collections4.CollectionUtils;
-import org.reflections.Reflections;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.annotation.PostConstruct;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * <p>
- * 展示实现BaseEnum接口的所有枚举值
- * </p>
- *
- * @author geekidea
- * @date 2018/11/02
- */
-@RestController
-@Slf4j
-@OperationLogIgnore
-@Api(value = "枚举字典", tags = {"枚举字典"})
-public class EnumController {
-
-    private static final List<String> FRAMEWORK_ENUM_PACKAGES = Arrays.asList(
-            "io.geekidea.springbootplus.framework.common.enums",
-            "io.geekidea.springbootplus.system.enums");
-
-    /**
-     * 枚举包路径
-     */
-    @Value("${spring-boot-plus.enum-packages}")
-    private List<String> enumPackages;
-
-    @GetMapping("/enum")
-    public ApiResult<Map<String, Map<Integer, EnumVo<? extends BaseEnum>>>> enumList() {
-        log.debug("enumList...");
-        return ApiResult.ok(BaseEnumUtil.getEnumMap());
-    }
-
-    @PostConstruct
-    public void init() {
-        try {
-            if (enumPackages == null) {
-                enumPackages = new ArrayList<>();
-            }
-            enumPackages.addAll(FRAMEWORK_ENUM_PACKAGES);
-
-            // 获取BaseEnum接口的所有实现
-            log.debug("enumPackages:" + enumPackages);
-            Reflections reflections = new Reflections(enumPackages);
-            Set<Class<? extends BaseEnum>> set = reflections.getSubTypesOf(BaseEnum.class);
-            if (CollectionUtils.isEmpty(set)) {
-                return;
-            }
-            // 循环获取BaseEnum枚举
-            for (Class<? extends BaseEnum> clazz : set) {
-                BaseEnum[] enumConstants = clazz.getEnumConstants();
-                Map<Integer, EnumVo<? extends BaseEnum>> enumVoMap = new ConcurrentHashMap<>(enumConstants.length);
-                for (BaseEnum baseEnum : enumConstants) {
-                    Integer code = baseEnum.getCode();
-                    String desc = baseEnum.getDesc();
-                    EnumVo<BaseEnum> enumVo = new EnumVo<BaseEnum>()
-                            .setCode(code)
-                            .setDesc(desc)
-                            .setBaseEnum(baseEnum);
-                    enumVoMap.put(code, enumVo);
-                }
-                // 设置map
-                BaseEnumUtil.getEnumMap().put(clazz.getName(), enumVoMap);
-            }
-            log.debug("enumMap:{}", BaseEnumUtil.getEnumMap());
-        } catch (Exception e) {
-            log.error("获取BaseEnum枚举map异常", e);
-        }
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/controller/IndexController.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/controller/IndexController.java
deleted file mode 100644
index 9031b6ab..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/controller/IndexController.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.controller;
-
-import io.geekidea.springbootplus.framework.log.annotation.OperationLogIgnore;
-import io.swagger.annotations.Api;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
-
-/**
- * <p>
- * 项目根路径提示信息
- * </p>
- *
- * @author geekidea
- * @date 2018/11/12
- */
-@Slf4j
-@Controller
-@OperationLogIgnore
-@Api(value = "Index API", tags = {"Index"})
-public class IndexController {
-
-    @GetMapping("/")
-    public String home() {
-        return "redirect:/index.html";
-    }
-
-    /**
-     * SwaggerUI
-     */
-    @GetMapping("/docs")
-    public String swagger() {
-        return "redirect:/swagger-ui.html";
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/entity/BaseEntity.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/entity/BaseEntity.java
deleted file mode 100644
index 58d02fc7..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/entity/BaseEntity.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.entity;
-
-import io.swagger.annotations.ApiModel;
-
-import java.io.Serializable;
-
-/**
- * 实体父类
- * @author geekidea
- * @date 2018-11-08
- */
-@ApiModel("BaseEntity")
-public abstract class BaseEntity implements Serializable{
-	private static final long serialVersionUID = -7176390653391227433L;
-	
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/enums/BaseEnum.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/enums/BaseEnum.java
deleted file mode 100644
index dfcc1286..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/enums/BaseEnum.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.enums;
-
-
-/**
- * 枚举类型父接口
- *
- * @author geekidea
- * @date 2018-11-08
- */
-public interface BaseEnum {
-
-    /**
-     * 获取枚举标识
-     *
-     * @return
-     */
-    Integer getCode();
-
-    /**
-     * 获取枚举描述
-     *
-     * @return
-     */
-    String getDesc();
-
-    /**
-     * 通过枚举类型和code值获取对应的枚举类型
-     * @param enumType
-     * @param code
-     * @param <T>
-     * @return
-     */
-    static <T extends BaseEnum> T valueOf(Class<? extends BaseEnum> enumType, Integer code) {
-        if (enumType == null || code == null) {
-            return null;
-        }
-        T[] enumConstants = (T[]) enumType.getEnumConstants();
-        if (enumConstants == null) {
-            return null;
-        }
-        for (T enumConstant : enumConstants) {
-            int enumCode = enumConstant.getCode();
-            if (code.equals(enumCode)) {
-                return enumConstant;
-            }
-        }
-        return null;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/BusinessException.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/BusinessException.java
deleted file mode 100644
index 0b769d1e..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/BusinessException.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.exception;
-
-import io.geekidea.springbootplus.framework.common.api.ApiCode;
-
-/**
- * 业务异常
- *
- * @author geekidea
- * @date 2018-11-08
- */
-public class BusinessException extends SpringBootPlusException {
-	private static final long serialVersionUID = -2303357122330162359L;
-
-	public BusinessException(String message) {
-        super(message);
-    }
-
-    public BusinessException(Integer errorCode, String message) {
-        super(errorCode, message);
-    }
-
-    public BusinessException(ApiCode apiCode) {
-        super(apiCode);
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/DaoException.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/DaoException.java
deleted file mode 100644
index 015c5237..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/DaoException.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.exception;
-
-import io.geekidea.springbootplus.framework.common.api.ApiCode;
-
-/**
- * DAO异常
- *
- * @author geekidea
- * @date 2018-11-08
- */
-public class DaoException extends SpringBootPlusException {
-	private static final long serialVersionUID = -6912618737345878854L;
-
-	public DaoException(String message) {
-        super(message);
-    }
-
-    public DaoException(Integer errorCode, String message) {
-        super(errorCode, message);
-    }
-
-    public DaoException(ApiCode apiCode) {
-        super(apiCode);
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/GlobalErrorController.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/GlobalErrorController.java
deleted file mode 100644
index dbdc25de..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/GlobalErrorController.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.exception;
-
-import io.geekidea.springbootplus.framework.common.api.ApiCode;
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.web.servlet.error.ErrorController;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-import springfox.documentation.annotations.ApiIgnore;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-
-/**
- * 全局Error/404处理
- * @author geekidea
- * @date 2018-11-08
- */
-@ApiIgnore
-@RestController
-@Slf4j
-public class GlobalErrorController implements ErrorController {
-
-    private static final String ERROR_PATH = "/error";
-
-    @RequestMapping(ERROR_PATH)
-    public ApiResult<?> handleError(HttpServletRequest request,HttpServletResponse response){
-        int status = response.getStatus();
-        switch (status){
-            case HttpServletResponse.SC_UNAUTHORIZED:
-                return ApiResult.fail(ApiCode.UNAUTHORIZED);
-            case HttpServletResponse.SC_FORBIDDEN:
-                return ApiResult.fail(ApiCode.NOT_PERMISSION);
-            case HttpServletResponse.SC_NOT_FOUND:
-                return ApiResult.fail(ApiCode.NOT_FOUND);
-            default:
-                break;
-        }
-        return ApiResult.fail(ApiCode.FAIL);
-    }
-
-    @Override
-    public String getErrorPath() {
-        log.error("errorPath....");
-        return ERROR_PATH;
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/SpringBootPlusConfigException.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/SpringBootPlusConfigException.java
deleted file mode 100644
index a7e7af33..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/SpringBootPlusConfigException.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.exception;
-
-import io.geekidea.springbootplus.framework.common.api.ApiCode;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-
-/**
- * spring-boot-plus配置异常
- *
- * @author geekidea
- * @date 2020/3/21
- */
-@Data
-@EqualsAndHashCode(callSuper = true)
-public class SpringBootPlusConfigException extends SpringBootPlusException {
-
-    private static final long serialVersionUID = 8952028631871769425L;
-
-    private Integer errorCode;
-    private String message;
-
-    public SpringBootPlusConfigException() {
-        super();
-    }
-
-    public SpringBootPlusConfigException(String message) {
-        super(message);
-        this.message = message;
-    }
-
-    public SpringBootPlusConfigException(Integer errorCode, String message) {
-        super(message);
-        this.errorCode = errorCode;
-        this.message = message;
-    }
-
-    public SpringBootPlusConfigException(ApiCode apiCode) {
-        super(apiCode.getMessage());
-        this.errorCode = apiCode.getCode();
-        this.message = apiCode.getMessage();
-    }
-
-    public SpringBootPlusConfigException(String message, Throwable cause) {
-        super(message, cause);
-    }
-
-    public SpringBootPlusConfigException(Throwable cause) {
-        super(cause);
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/SpringBootPlusException.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/SpringBootPlusException.java
deleted file mode 100644
index 46dc1a7c..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/exception/SpringBootPlusException.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.exception;
-
-import io.geekidea.springbootplus.framework.common.api.ApiCode;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-
-/**
- * 自定义异常
- * @author geekidea
- * @date 2018-11-08
- */
-@Data
-@EqualsAndHashCode(callSuper = true)
-public class SpringBootPlusException extends RuntimeException{
-
-    private static final long serialVersionUID = -2470461654663264392L;
-
-    private Integer errorCode;
-    private String message;
-
-    public SpringBootPlusException() {
-        super();
-    }
-
-    public SpringBootPlusException(String message) {
-        super(message);
-        this.message = message;
-    }
-
-    public SpringBootPlusException(Integer errorCode, String message) {
-        super(message);
-        this.errorCode = errorCode;
-        this.message = message;
-    }
-
-    public SpringBootPlusException(ApiCode apiCode) {
-        super(apiCode.getMessage());
-        this.errorCode = apiCode.getCode();
-        this.message = apiCode.getMessage();
-    }
-
-    public SpringBootPlusException(String message, Throwable cause) {
-        super(message, cause);
-    }
-
-    public SpringBootPlusException(Throwable cause) {
-        super(cause);
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/param/IdParam.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/param/IdParam.java
deleted file mode 100644
index e5e706d8..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/param/IdParam.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.param;
-
-import io.swagger.annotations.ApiModel;
-import lombok.Data;
-
-import javax.validation.constraints.NotNull;
-import java.io.Serializable;
-
-/**
- * ID参数
- *
- * @author geekidea
- * @date 2018-11-08
- */
-@Data
-@ApiModel("ID参数")
-public class IdParam implements Serializable {
-    private static final long serialVersionUID = -5353973980674510450L;
-
-    @NotNull(message = "ID不能为空")
-    private Long id;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/service/BaseService.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/service/BaseService.java
deleted file mode 100644
index 10c4a3f9..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/service/BaseService.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.service;
-
-import com.baomidou.mybatisplus.extension.service.IService;
-
-/**
- * 公共Service接口
- *
- * @author geekidea
- * @date 2018-11-08
- */
-public interface BaseService<T> extends IService<T> {
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/service/impl/BaseServiceImpl.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/service/impl/BaseServiceImpl.java
deleted file mode 100644
index ee5122c9..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/service/impl/BaseServiceImpl.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.service.impl;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import io.geekidea.springbootplus.framework.common.service.BaseService;
-import io.geekidea.springbootplus.framework.util.LambdaColumn;
-
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-
-/**
- * 公共Service父类
- *
- * @author geekidea
- * @date 2018-11-08
- */
-public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements BaseService<T> {
-
-    /**
-     * 实体类型
-     */
-    private Class<?> entityClass;
-
-    {
-        Class<?> clazz = this.getClass();
-        Type type = clazz.getGenericSuperclass();
-        if (type instanceof ParameterizedType) {
-            Type[] p = ((ParameterizedType) type).getActualTypeArguments();
-            this.entityClass = (Class<T>) p[1];
-        }
-    }
-
-    /**
-     * 获取对应字段的数据表列名称
-     *
-     * @param func
-     * @return
-     */
-    public String getLambdaColumn(SFunction<T, ?> func) {
-        return new LambdaColumn<T>().get(func);
-    }
-
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/common/vo/EnumVo.java b/framework/src/main/java/io/geekidea/springbootplus/framework/common/vo/EnumVo.java
deleted file mode 100644
index e5161876..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/common/vo/EnumVo.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.common.vo;
-
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-/**
- * 枚举类型VO
- *
- * @author geekidea
- * @date 2019-11-02
- **/
-@Data
-@Accessors(chain = true)
-public class EnumVo<T> {
-
-    /**
-     * 枚举code
-     */
-    private Integer code;
-
-    /**
-     * 枚举描述
-     */
-    private String desc;
-
-    /**
-     * 枚举类型
-     */
-    private T baseEnum;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/ConverterConfig.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/ConverterConfig.java
deleted file mode 100644
index e1d9b7fb..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/ConverterConfig.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.converter;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.core.convert.converter.Converter;
-
-import java.util.Date;
-
-/**
- * 转换器配置
- * @author geekidea
- * @date 2018-11-08
- */
-@Configuration
-public class ConverterConfig {
-
-    @Bean
-    public Converter<String, Date> stringToDateConverter() {
-        return new StringToDateConverter();
-    }
-
-    @Bean
-    public Converter<String, Integer> stringToIntegerConverter() {
-        return new StringToIntegerConverter();
-    }
-
-    @Bean
-    public Converter<String, Double> stringToDoubleConverter() {
-        return new StringToDoubleConverter();
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToDateConverter.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToDateConverter.java
deleted file mode 100644
index 8c54b50b..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToDateConverter.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.converter;
-
-import org.springframework.core.convert.converter.Converter;
-
-import java.util.Date;
-
-/**
- * <code>
- * 日期转换器,将请求参数的日期字符串转换成java.util.Date类型
- * </code>
- * @author geekidea
- * @date 2018-11-08
- */
-public class StringToDateConverter implements Converter<String, Date> {
-
-	@Override
-	public Date convert(String source) {
-		return StringToDateUtil.convert(source);
-	}
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToDateUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToDateUtil.java
deleted file mode 100644
index bc686d2c..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToDateUtil.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.converter;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-/**
- * <code>
- * <pre>
- * 日期转换器,将请求参数的日期字符串转换成java.util.Date类型
- * 日期格式顺序:
- * 	1.yyyy-MM-dd HH:mm:ss:S
- * 	2.yyyy-MM-dd HH:mm:ss
- * 	3.yyyy-MM-dd HH:mm
- * 	4.yyyy-MM-dd HH
- * 	5.yyyy-MM-dd
- * </pre>
- * </code>
- *
- * @author geekidea
- * @date 2018-11-08
- */
-public class StringToDateUtil {
-
-    /**
-     * 时间戳字符长度，不包含毫秒
-     */
-    private static final Integer TIMESTAMP_LENGTH = 10;
-
-    /**
-     * 日期格式化数组
-     */
-    private static DateFormat[] dateFormats = {
-            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:S"),
-            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"),
-            new SimpleDateFormat("yyyy-MM-dd HH:mm"),
-            new SimpleDateFormat("yyyy-MM-dd HH"),
-            new SimpleDateFormat("yyyy-MM-dd"),
-            new SimpleDateFormat("yyyy-MM")
-    };
-
-    /**
-     * <code>
-     * <pre>
-     * 1.如果日期字符串为空,则直接返回空
-     * 2.使用格式化组进行格式化,如果解析成功,则直接返回
-     * 4.否则,抛出非法参数异常
-     * @param source 请求的日期参数
-     * @return 解析后的日期类型:java.util.Date
-     * @exception IllegalArgumentException 非法参数异常
-     * </pre>
-     * </code>
-     */
-    public static Date convert(String source) {
-        if (StringUtils.isBlank(source)) {
-            return null;
-        }
-        source = source.trim();
-
-        try {
-            int timeLength = source.length();
-            Long time = Long.parseLong(source);
-            if (timeLength == TIMESTAMP_LENGTH) {
-                time = time * 1000;
-            }
-            Date date = new Date(time);
-            return date;
-        } catch (Exception e) {
-
-        }
-
-        Date date = null;
-        boolean flag = false;
-        for (DateFormat dateFormat : dateFormats) {
-            try {
-                date = dateFormat.parse(source);
-                flag = true;
-                break;
-            } catch (ParseException e) {
-            }
-        }
-
-        if (flag) {
-            return date;
-        } else {
-            throw new IllegalArgumentException("不能解析日期:" + source);
-        }
-
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToDoubleConverter.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToDoubleConverter.java
deleted file mode 100644
index 071b08d6..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToDoubleConverter.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.converter;
-
-import org.springframework.core.convert.converter.Converter;
-
-/**
- * <code>
- *
- * </code>
- * @author geekidea
- * @date 2018-11-08
- */
-public class StringToDoubleConverter implements Converter<String, Double> {
-
-	@Override
-	public Double convert(String source) {
-		return StringToDoubleUtil.convert(source);
-	}
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToDoubleUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToDoubleUtil.java
deleted file mode 100644
index b8c90ea8..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToDoubleUtil.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.converter;
-
-
-import org.apache.commons.lang3.StringUtils;
-
-/**
- * <code>
- * <pre>
- * 空字符串("")转换成Double的null
- *
- * </pre>
- * </code>
- * @author geekidea
- * @date 2018-11-08
- */
-public class StringToDoubleUtil {
-
-	public static Double convert(String source) {
-		if (StringUtils.isBlank(source)){
-			return null;
-		}
-		Double d = Double.parseDouble(source);
-		return d;
-	}
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToIntegerConverter.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToIntegerConverter.java
deleted file mode 100644
index fad31323..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToIntegerConverter.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.converter;
-
-import org.springframework.core.convert.converter.Converter;
-
-/**
- * <code>
- *
- * </code>
- * @author geekidea
- * @date 2018-11-08
- */
-public class StringToIntegerConverter implements Converter<String, Integer> {
-
-	@Override
-	public Integer convert(String source) {
-		return StringToIntegerUtil.convert(source);
-	}
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToIntegerUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToIntegerUtil.java
deleted file mode 100644
index e57da5cc..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/converter/StringToIntegerUtil.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.converter;
-
-import org.apache.commons.lang3.StringUtils;
-
-/**
- * <code>
- * <pre>
- * 空字符串("")转换成Integer的null
- *
- * </pre>
- * </code>
- * @author geekidea
- * @date 2018-11-08
- */
-public class StringToIntegerUtil {
-
-	public static Integer convert(String source) {
-		if (StringUtils.isBlank(source)){
-			return null;
-		}
-		Integer i = Integer.parseInt(source);
-		return i;
-	}
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/deserializer/JacksonDateDeserializer.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/deserializer/JacksonDateDeserializer.java
deleted file mode 100644
index a72637e7..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/deserializer/JacksonDateDeserializer.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.jackson.deserializer;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import io.geekidea.springbootplus.framework.config.converter.StringToDateUtil;
-
-import java.io.IOException;
-import java.util.Date;
-
-/**
- * <p>
- *     Jackson Date序列化器
- * </p>
- * @author geekidea
- * @date 2018-11-08
- */
-public class JacksonDateDeserializer extends JsonDeserializer<Date> {
-    @Override
-    public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
-        String date = jp.getText();
-        return StringToDateUtil.convert(date);
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/deserializer/JacksonDoubleDeserializer.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/deserializer/JacksonDoubleDeserializer.java
deleted file mode 100644
index 40071291..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/deserializer/JacksonDoubleDeserializer.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.jackson.deserializer;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import io.geekidea.springbootplus.framework.config.converter.StringToDoubleUtil;
-
-import java.io.IOException;
-
-/**
- * @author geekidea
- * @date 2018-11-08
- */
-public class JacksonDoubleDeserializer extends JsonDeserializer<Double> {
-
-    @Override
-    public Double deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
-        String string = jsonParser.getText();
-        return StringToDoubleUtil.convert(string);
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/deserializer/JacksonLocalDateTimeDeserializer.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/deserializer/JacksonLocalDateTimeDeserializer.java
deleted file mode 100644
index 8fc5071c..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/deserializer/JacksonLocalDateTimeDeserializer.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.jackson.deserializer;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import io.geekidea.springbootplus.config.constant.DatePattern;
-import org.apache.commons.lang3.StringUtils;
-
-import java.io.IOException;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-
-/**
- * <p>
- *     Jackson LocaDateTime反序列化器
- * </p>
- * @author geekidea
- * @date 2018-11-08
- */
-public class JacksonLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
-
-    @Override
-    public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
-        String string = jp.getText();
-        if (StringUtils.isBlank(string)){
-            return null;
-        }
-        return LocalDateTime.parse(string, DateTimeFormatter.ofPattern(DatePattern.YYYY_MM_DD_HH_MM_SS));
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/serializer/JacksonDateSerializer.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/serializer/JacksonDateSerializer.java
deleted file mode 100644
index 394441a9..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/serializer/JacksonDateSerializer.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.jackson.serializer;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import io.geekidea.springbootplus.framework.util.DateUtil;
-
-import java.io.IOException;
-import java.util.Date;
-
-/**
- * <p>
- * Jackson Date反序列化器
- * </p>
- *
- * @author geekidea
- * @date 2018-11-08
- */
-public class JacksonDateSerializer extends JsonSerializer<Date> {
-    @Override
-    public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
-        String string = null;
-        if (date != null) {
-            string = DateUtil.getDateTimeString(date);
-        }
-        jsonGenerator.writeString(string);
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/serializer/JacksonIntegerDeserializer.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/serializer/JacksonIntegerDeserializer.java
deleted file mode 100644
index 785b02be..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/serializer/JacksonIntegerDeserializer.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.jackson.serializer;
-
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import io.geekidea.springbootplus.framework.config.converter.StringToIntegerUtil;
-
-import java.io.IOException;
-
-/**
- * @author geekidea
- * @date 2018-11-08
- */
-public class JacksonIntegerDeserializer extends JsonDeserializer<Integer> {
-
-    @Override
-    public Integer deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
-        String string = jsonParser.getText();
-        return StringToIntegerUtil.convert(string);
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/serializer/JacksonLocalDateTimeSerializer.java b/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/serializer/JacksonLocalDateTimeSerializer.java
deleted file mode 100644
index fb72cdd6..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/config/jackson/serializer/JacksonLocalDateTimeSerializer.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.config.jackson.serializer;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import io.geekidea.springbootplus.config.constant.DatePattern;
-
-import java.io.IOException;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-
-/**
- * <p>
- * Jackson LocalDateTime 自定义序列化器
- * </p>
- *
- * @author geekidea
- * @date 2018/11/8
- */
-public class JacksonLocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
-
-    @Override
-    public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
-        String string = null;
-        if (localDateTime != null) {
-            string = localDateTime.format(DateTimeFormatter.ofPattern(DatePattern.YYYY_MM_DD_HH_MM_SS));
-        }
-        jsonGenerator.writeString(string);
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/bean/RequestDetail.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/bean/RequestDetail.java
deleted file mode 100644
index b84b7da3..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/bean/RequestDetail.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.bean;
-
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-
-/**
- * Filter请求详情信息
- *
- * @author geekidea
- * @date 2020/3/26
- **/
-@Data
-@Accessors(chain = true)
-public class RequestDetail implements Serializable {
-	private static final long serialVersionUID = 2543641512850125440L;
-
-	/**
-     * 请求ip地址
-     */
-    private String ip;
-
-    /**
-     * 请求路径
-     */
-    private String path;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/filter/RequestDetailFilter.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/filter/RequestDetailFilter.java
deleted file mode 100644
index b76988b6..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/filter/RequestDetailFilter.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.filter;
-
-import io.geekidea.springbootplus.framework.core.bean.RequestDetail;
-import io.geekidea.springbootplus.framework.core.util.RequestDetailThreadLocal;
-import io.geekidea.springbootplus.framework.util.IpUtil;
-import lombok.extern.slf4j.Slf4j;
-
-import javax.servlet.*;
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-
-/**
- * 请求详情信息Filter
- *
- * @author geekidea
- * @date 2020/3/25
- **/
-@Slf4j
-public class RequestDetailFilter implements Filter {
-
-    @Override
-    public void init(FilterConfig filterConfig) throws ServletException {
-        log.info("RequestDetailFilter init");
-    }
-
-    @Override
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-        // 设置请求详情信息
-        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
-        // 请求IP
-        String ip = IpUtil.getRequestIp(httpServletRequest);
-        // 请求路径
-        String path = httpServletRequest.getRequestURI();
-        RequestDetail requestDetail = new RequestDetail()
-                .setIp(ip)
-                .setPath(path);
-        // 设置请求详情信息
-        RequestDetailThreadLocal.setRequestDetail(requestDetail);
-        chain.doFilter(request, response);
-        // 释放
-        RequestDetailThreadLocal.remove();
-    }
-
-    @Override
-    public void destroy() {
-        log.info("RequestDetailFilter destroy");
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/interceptor/PermissionInterceptor.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/interceptor/PermissionInterceptor.java
deleted file mode 100644
index a3cfab07..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/interceptor/PermissionInterceptor.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.interceptor;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * 自定义权限拦截器
- *
- * @author geekidea
- * @since 2018-11-08
- */
-@Slf4j
-@ConditionalOnProperty(value = {"spring-boot-plus.interceptor.permission.enable"}, matchIfMissing = true)
-public class PermissionInterceptor extends HandlerInterceptorAdapter {
-
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
-            throws Exception {
-        return true;
-    }
-
-    @Override
-    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
-
-    }
-
-}
\ No newline at end of file
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/BasePageOrderParam.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/BasePageOrderParam.java
deleted file mode 100644
index 1d32e24f..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/BasePageOrderParam.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.pagination;
-
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import org.apache.commons.collections.CollectionUtils;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * 可排序查询参数对象
- *
- * @author geekidea
- * @since 2019-08-04
- */
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ApiModel("可排序查询参数对象")
-public abstract class BasePageOrderParam extends BasePageParam {
-    private static final long serialVersionUID = 57714391204790143L;
-
-    @ApiModelProperty("排序")
-    private List<OrderItem> pageSorts;
-
-    public void defaultPageSort(OrderItem orderItem) {
-        this.defaultPageSorts(Arrays.asList(orderItem));
-    }
-
-    public void defaultPageSorts(List<OrderItem> pageSorts) {
-        if (CollectionUtils.isEmpty(pageSorts)) {
-            return;
-        }
-        this.pageSorts = pageSorts;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/BasePageParam.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/BasePageParam.java
deleted file mode 100644
index 99b8b9aa..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/BasePageParam.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.pagination;
-
-import io.geekidea.springbootplus.config.constant.CommonConstant;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import java.io.Serializable;
-
-/**
- * 查询参数
- *
- * @author geekidea
- * @since 2018-11-08
- */
-@Data
-@ApiModel("查询参数对象")
-public abstract class BasePageParam implements Serializable {
-    private static final long serialVersionUID = -3263921252635611410L;
-
-    @ApiModelProperty(value = "页码,默认为1", example = "1")
-    private Long pageIndex = CommonConstant.DEFAULT_PAGE_INDEX;
-
-    @ApiModelProperty(value = "页大小,默认为10", example = "10")
-    private Long pageSize = CommonConstant.DEFAULT_PAGE_SIZE;
-
-    @ApiModelProperty(value = "搜索字符串", example = "")
-    private String keyword;
-
-    public void setPageIndex(Long pageIndex) {
-        if (pageIndex == null || pageIndex <= 0) {
-            this.pageIndex = CommonConstant.DEFAULT_PAGE_INDEX;
-        } else {
-            this.pageIndex = pageIndex;
-        }
-    }
-
-    public void setPageSize(Long pageSize) {
-        if (pageSize == null || pageSize <= 0) {
-            this.pageSize = CommonConstant.DEFAULT_PAGE_SIZE;
-        } else {
-            this.pageSize = pageSize;
-        }
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/OrderMapping.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/OrderMapping.java
deleted file mode 100644
index cd817eea..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/OrderMapping.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.pagination;
-
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.google.common.base.CaseFormat;
-import io.geekidea.springbootplus.framework.util.PropertyColumnUtil;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.MapUtils;
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * 排序列映射
- *
- * @author geekidea
- * @date 2020/3/14
- **/
-@Data
-@Accessors(chain = true)
-public class OrderMapping {
-
-    private boolean underLineMode;
-
-    public OrderMapping() {
-
-    }
-
-    public OrderMapping(boolean underLineMode) {
-        this.underLineMode = underLineMode;
-    }
-
-    private Map<String, String> map = new ConcurrentHashMap<>();
-
-    public OrderMapping mapping(String property, String column) {
-        map.put(property, column);
-        return this;
-    }
-
-    public OrderMapping mapping(String property, String tablePrefix, String column) {
-        if (StringUtils.isNotBlank(tablePrefix)) {
-            column = tablePrefix + "." + column;
-        }
-        map.put(property, column);
-        return this;
-    }
-
-
-    public OrderMapping mapping(String property, Class<?> clazz) {
-        String column = PropertyColumnUtil.getColumn(clazz, property);
-        map.put(property, column);
-        return this;
-    }
-
-    public OrderMapping mapping(String property, String tablePrefix, Class<?> clazz) {
-        String column = PropertyColumnUtil.getColumn(clazz, property);
-        mapping(property, tablePrefix, column);
-        return this;
-    }
-
-    public String getMappingColumn(String property) {
-        if (StringUtils.isBlank(property)) {
-            return null;
-        }
-        return map.get(property);
-    }
-
-    public void filterOrderItems(List<OrderItem> orderItems) {
-        if (CollectionUtils.isEmpty(orderItems)) {
-            return;
-        }
-        // 如果集合不为空，则按照PropertyColumnUtil映射
-        if (MapUtils.isNotEmpty(map)) {
-            orderItems.forEach(item -> {
-                item.setColumn(this.getMappingColumn(item.getColumn()));
-            });
-        } else if (underLineMode) {
-            // 如果开启下划线模式，自动转换成下划线
-            orderItems.forEach(item -> {
-                String column = item.getColumn();
-                if (StringUtils.isNotBlank(column)) {
-                    // 驼峰转换成下划线
-                    item.setColumn(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, column));
-                }
-            });
-        }
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/PageInfo.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/PageInfo.java
deleted file mode 100644
index 5f98428d..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/PageInfo.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.pagination;
-
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-import org.apache.commons.collections4.CollectionUtils;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * 自定义分页参数
- *
- * @author geekidea
- * @date 2020/3/27
- **/
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-public class PageInfo<T> extends Page<T> {
-	private static final long serialVersionUID = -2211095086394170578L;
-
-	/**
-     * 分页参数
-     */
-    private BasePageParam pageParam;
-
-    /**
-     * 默认排序字段信息
-     */
-    private OrderItem defaultOrderItem;
-
-    /**
-     * 排序字段映射
-     */
-    private OrderMapping orderMapping;
-
-
-    public PageInfo() {
-
-    }
-
-    /**
-     * 传入分页参数
-     *
-     * @param pageParam
-     */
-    public PageInfo(BasePageParam pageParam) {
-        this(pageParam, null, null);
-    }
-
-    /**
-     * 传入分页参数，默认排序
-     *
-     * @param basePageParam
-     * @param defaultOrderItem
-     */
-    public PageInfo(BasePageParam basePageParam, OrderItem defaultOrderItem) {
-        this(basePageParam, defaultOrderItem, null);
-    }
-
-    /**
-     * 传入分页参数，排序字段映射
-     *
-     * @param pageParam
-     * @param orderMapping
-     */
-    public PageInfo(BasePageParam pageParam, OrderMapping orderMapping) {
-        this(pageParam, null, orderMapping);
-    }
-
-    /**
-     * 传入分页参数，默认排序，排序字段映射
-     *
-     * @param pageParam
-     * @param defaultOrderItem
-     * @param orderMapping
-     */
-    public PageInfo(BasePageParam pageParam, OrderItem defaultOrderItem, OrderMapping orderMapping) {
-        this.pageParam = pageParam;
-        this.defaultOrderItem = defaultOrderItem;
-        this.orderMapping = orderMapping;
-        this.handle();
-    }
-
-    /**
-     * 分页构造函数
-     *
-     * @param current 当前页
-     * @param size    每页显示条数
-     */
-    public PageInfo(long current, long size) {
-        super(current, size, 0);
-    }
-
-    public PageInfo(long current, long size, long total) {
-        super(current, size, total, true);
-    }
-
-    public PageInfo(long current, long size, boolean isSearchCount) {
-        super(current, size, isSearchCount);
-    }
-
-    public PageInfo(long current, long size, long total, boolean isSearchCount) {
-        super(current, size, total, isSearchCount);
-    }
-
-    /**
-     * 如果是pageParam是OrderPageParam，并且不为空，则使用前端排序
-     * 否则使用默认排序
-     */
-    private void handle() {
-        if (pageParam == null) {
-            return;
-        }
-        // 设置pageIndex/pageSize
-        super.setCurrent(pageParam.getPageIndex());
-        super.setSize(pageParam.getPageSize());
-        // 排序字段处理
-        BasePageOrderParam basePageOrderParam = (BasePageOrderParam) pageParam;
-        List<OrderItem> orderItems = basePageOrderParam.getPageSorts();
-        if (CollectionUtils.isEmpty(orderItems)) {
-            setDefaultOrder(defaultOrderItem);
-            return;
-        }
-        if (orderMapping == null) {
-            orderMapping = new OrderMapping(true);
-        }
-        orderMapping.filterOrderItems(orderItems);
-        super.setOrders(orderItems);
-    }
-
-    /**
-     * 设置默认排序
-     *
-     * @param defaultOrderItem
-     * @return
-     */
-    public PageInfo<T> setDefaultOrder(OrderItem defaultOrderItem) {
-        if (defaultOrderItem != null) {
-            this.defaultOrderItem = defaultOrderItem;
-            super.setOrders(Arrays.asList(defaultOrderItem));
-        }
-        return this;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/Paging.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/Paging.java
deleted file mode 100644
index 515b3496..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/pagination/Paging.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.pagination;
-
-import com.alibaba.fastjson.annotation.JSONField;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.geekidea.springbootplus.config.constant.CommonConstant;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.extern.slf4j.Slf4j;
-
-import java.io.Serializable;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * 分页结果对象
- *
- * @author geekidea
- * @date 2018-11-08
- */
-
-@Slf4j
-@Data
-@ApiModel("分页结果对象")
-public class Paging<T> implements Serializable {
-    private static final long serialVersionUID = 4784961132604516495L;
-
-    @ApiModelProperty("总行数")
-    @JSONField(name = CommonConstant.PAGE_TOTAL_NAME)
-    @JsonProperty(CommonConstant.PAGE_TOTAL_NAME)
-    private long total = 0;
-
-    @ApiModelProperty("数据列表")
-    @JSONField(name = CommonConstant.PAGE_RECORDS_NAME)
-    @JsonProperty(CommonConstant.PAGE_RECORDS_NAME)
-    private List<T> records = Collections.emptyList();
-
-    @ApiModelProperty(value = "页码")
-    @JSONField(name = CommonConstant.PAGE_INDEX_NAME)
-    @JsonProperty(CommonConstant.PAGE_INDEX_NAME)
-    private Long pageIndex;
-
-    @ApiModelProperty(value = "页大小")
-    @JSONField(name = CommonConstant.PAGE_SIZE_NAME)
-    @JsonProperty(CommonConstant.PAGE_SIZE_NAME)
-    private Long pageSize;
-
-    public Paging() {
-
-    }
-
-    public Paging(IPage<T> page) {
-        this.total = page.getTotal();
-        this.records = page.getRecords();
-        this.pageIndex = page.getCurrent();
-        this.pageSize = page.getSize();
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/util/RequestDetailThreadLocal.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/util/RequestDetailThreadLocal.java
deleted file mode 100644
index a4ba79c7..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/util/RequestDetailThreadLocal.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.util;
-
-import io.geekidea.springbootplus.framework.core.bean.RequestDetail;
-
-/**
- * 记录请求详情信息到当前线程中，可在任何地方获取
- *
- * @author geekidea
- * @date 2020/3/26
- **/
-public class RequestDetailThreadLocal {
-
-    private static ThreadLocal<RequestDetail> threadLocal = new ThreadLocal<>();
-
-    /**
-     * 设置请求信息到当前线程中
-     *
-     * @param requestDetail
-     */
-    public static void setRequestDetail(RequestDetail requestDetail) {
-        threadLocal.set(requestDetail);
-    }
-
-    /**
-     * 从当前线程中获取请求信息
-     */
-    public static RequestDetail getRequestDetail() {
-        return threadLocal.get();
-    }
-
-    /**
-     * 销毁
-     */
-    public static void remove() {
-        threadLocal.remove();
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/EnumTypeValidator.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/EnumTypeValidator.java
deleted file mode 100644
index 4853e1ad..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/EnumTypeValidator.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.validator;
-
-import io.geekidea.springbootplus.framework.common.enums.BaseEnum;
-import io.geekidea.springbootplus.framework.common.exception.BusinessException;
-import io.geekidea.springbootplus.framework.core.validator.constraints.EnumType;
-import io.geekidea.springbootplus.framework.util.BaseEnumUtil;
-
-import javax.validation.ConstraintValidator;
-import javax.validation.ConstraintValidatorContext;
-
-/**
- * 自定义系统内的枚举验证注解实现类
- *
- * @author geekidea
- * @date 2018-11-08
- */
-public class EnumTypeValidator implements ConstraintValidator<EnumType, Integer> {
-
-    private Class<? extends BaseEnum> baseEnum;
-
-    @Override
-    public void initialize(EnumType parameters) {
-        baseEnum = parameters.type();
-        if (baseEnum == null) {
-            throw new BusinessException("请传入枚举类型类");
-        }
-    }
-
-    @Override
-    public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
-        if (value == null) {
-            return true;
-        }
-        return BaseEnumUtil.exists(baseEnum, value);
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/IdCardValidator.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/IdCardValidator.java
deleted file mode 100644
index fd1476ba..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/IdCardValidator.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.validator;
-
-import io.geekidea.springbootplus.framework.core.validator.constraints.IdCard;
-
-import javax.validation.ConstraintValidator;
-import javax.validation.ConstraintValidatorContext;
-import java.util.regex.Pattern;
-
-/**
- * 自定义身份证号码验证注解实现类
- * @author geekidea
- * @date 2018-11-08
- */
-public class IdCardValidator implements ConstraintValidator<IdCard, String> {
-	private static final String REG_EX = "(^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{2}[0-9Xx]$)|(^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}[0-9Xx]$)";
-	private static final Pattern PATTERN = Pattern.compile(REG_EX);
-
-	@Override
-	public void initialize(IdCard parameters) {
-	}
-
-	@Override
-	public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
-		if (value ==null){
-			return true;
-		}
-		return PATTERN.matcher(value).matches();
-	}
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/PhoneValidator.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/PhoneValidator.java
deleted file mode 100644
index a81ef49a..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/PhoneValidator.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.validator;
-
-import io.geekidea.springbootplus.framework.core.validator.constraints.Phone;
-
-import javax.validation.ConstraintValidator;
-import javax.validation.ConstraintValidatorContext;
-import java.util.regex.Pattern;
-
-/**
- * 自定义手机号码验证注解实现类
- * @author geekidea
- * @date 2018-11-08
- */
-public class PhoneValidator implements ConstraintValidator<Phone, String> {
-
-	private static final String REG_EX = "^1[3,4,5,6,7,8,9]\\d{9}$";
-	private static final Pattern PATTERN = Pattern.compile(REG_EX);
-
-	@Override
-	public void initialize(Phone parameters) {
-	}
-
-	@Override
-	public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
-		if (value ==null){
-			return true;
-		}
-		return PATTERN.matcher(value).matches();
-	}
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/constraints/EnumType.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/constraints/EnumType.java
deleted file mode 100644
index f42908f1..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/constraints/EnumType.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.validator.constraints;
-
-import io.geekidea.springbootplus.framework.common.enums.BaseEnum;
-import io.geekidea.springbootplus.framework.core.validator.EnumTypeValidator;
-
-import javax.validation.Constraint;
-import javax.validation.Payload;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * 枚举类型注解
- * @author geekidea
- * @date 2018-11-08
- */
-@Documented
-@Constraint(validatedBy = { EnumTypeValidator.class })
-@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
-@Retention(RUNTIME)
-public @interface EnumType {
-	String message() default "请输入正确的类型值";
-
-	Class<? extends BaseEnum> type();
-
-	Class<?>[] groups() default { };
-
-	Class<? extends Payload>[] payload() default { };
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/constraints/IdCard.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/constraints/IdCard.java
deleted file mode 100644
index 5e33d3c0..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/constraints/IdCard.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.validator.constraints;
-
-import io.geekidea.springbootplus.framework.core.validator.IdCardValidator;
-
-import javax.validation.Constraint;
-import javax.validation.Payload;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * 自定义身份证号码正则验证注解
- * @author geekidea
- * @date 2018-11-08
- */
-@Documented
-@Constraint(validatedBy = { IdCardValidator.class })
-@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
-@Retention(RUNTIME)
-public @interface IdCard {
-	String message() default "请输入有效的身份证号码";
-
-	Class<?>[] groups() default { };
-
-	Class<? extends Payload>[] payload() default { };
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/constraints/Phone.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/constraints/Phone.java
deleted file mode 100644
index 7f2aacfe..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/constraints/Phone.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.validator.constraints;
-
-import io.geekidea.springbootplus.framework.core.validator.PhoneValidator;
-
-import javax.validation.Constraint;
-import javax.validation.Payload;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * 自定义手机号码正则验证注解
- * @author geekidea
- * @date 2018-11-08
- */
-@Documented
-@Constraint(validatedBy = { PhoneValidator.class })
-@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
-@Retention(RUNTIME)
-public @interface Phone {
-	String message() default "请输入有效的手机号码";
-
-	Class<?>[] groups() default { };
-
-	Class<? extends Payload>[] payload() default { };
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/groups/Add.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/groups/Add.java
deleted file mode 100644
index ace305dc..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/groups/Add.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.validator.groups;
-
-import javax.validation.groups.Default;
-
-/**
- * Validator分组验证：添加
- *
- * @author geekidea
- * @date 2020/3/8
- **/
-public interface Add extends Default {
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/groups/Update.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/groups/Update.java
deleted file mode 100644
index 9e7d41f9..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/validator/groups/Update.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.validator.groups;
-
-import javax.validation.groups.Default;
-
-/**
- * Validator分组验证：修改
- *
- * @author geekidea
- * @date 2020/3/8
- **/
-public interface Update extends Default {
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/xss/XssJacksonSerializer.java b/framework/src/main/java/io/geekidea/springbootplus/framework/core/xss/XssJacksonSerializer.java
deleted file mode 100644
index 536329df..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/xss/XssJacksonSerializer.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.xss;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.text.StringEscapeUtils;
-
-import java.io.IOException;
-
-/**
- * Jackson响应参数字符串转义处理
- *
- * @author geekidea
- * @date 2019-10-10
- * @since 1.3.1.RELEASE
- **/
-@Slf4j
-public class XssJacksonSerializer extends JsonSerializer<String> {
-
-    @Override
-    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
-        jsonGenerator.writeString(StringEscapeUtils.escapeHtml4(s));
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/ip/entity/IpAddress.java b/framework/src/main/java/io/geekidea/springbootplus/framework/ip/entity/IpAddress.java
deleted file mode 100644
index 873838f3..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/ip/entity/IpAddress.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.ip.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-/**
- * IP地址
- *
- * @author geekidea
- * @since 2020-03-25
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "IpAddress对象")
-public class IpAddress extends BaseEntity {
-    private static final long serialVersionUID = 1L;
-
-    @TableId(value = "id", type = IdType.AUTO)
-    private Long id;
-
-    private String ipStart;
-
-    private String ipEnd;
-
-    @ApiModelProperty("区域")
-    private String area;
-
-    @ApiModelProperty("运营商")
-    private String operator;
-
-    private Long ipStartNum;
-
-    private Long ipEndNum;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/ip/mapper/IpAddressMapper.java b/framework/src/main/java/io/geekidea/springbootplus/framework/ip/mapper/IpAddressMapper.java
deleted file mode 100644
index d92f2b19..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/ip/mapper/IpAddressMapper.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.ip.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import io.geekidea.springbootplus.framework.ip.entity.IpAddress;
-import org.apache.ibatis.annotations.Param;
-import org.springframework.stereotype.Repository;
-
-/**
- * IP地址 Mapper 接口
- *
- * @author geekidea
- * @since 2020-03-25
- */
-@Repository
-public interface IpAddressMapper extends BaseMapper<IpAddress> {
-
-    /**
-     * 通过ip地址获取IP对象
-     *
-     * @param ip
-     * @return
-     */
-    IpAddress getByIp(@Param("ip") String ip);
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/ip/service/IpAddressService.java b/framework/src/main/java/io/geekidea/springbootplus/framework/ip/service/IpAddressService.java
deleted file mode 100644
index 9701a809..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/ip/service/IpAddressService.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.ip.service;
-
-import io.geekidea.springbootplus.framework.common.service.BaseService;
-import io.geekidea.springbootplus.framework.ip.entity.IpAddress;
-
-/**
- * IP地址 服务类
- *
- * @author geekidea
- * @since 2020-03-25
- */
-public interface IpAddressService extends BaseService<IpAddress> {
-
-    /**
-     * 通过ip地址获取IP对象
-     *
-     * @param ip
-     * @return
-     */
-    IpAddress getByIp(String ip);
-
-    /**
-     * 通过ip地址获取区域
-     *
-     * @param ip
-     * @return
-     */
-    String getAreaByIp(String ip);
-
-    /**
-     * 通过ip地址获取运营商
-     *
-     * @param ip
-     * @return
-     */
-    String getOperatorByIp(String ip);
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/ip/service/impl/IpAddressServiceImpl.java b/framework/src/main/java/io/geekidea/springbootplus/framework/ip/service/impl/IpAddressServiceImpl.java
deleted file mode 100644
index 2c17bd00..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/ip/service/impl/IpAddressServiceImpl.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.ip.service.impl;
-
-import io.geekidea.springbootplus.config.constant.CommonConstant;
-import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
-import io.geekidea.springbootplus.framework.ip.entity.IpAddress;
-import io.geekidea.springbootplus.framework.ip.mapper.IpAddressMapper;
-import io.geekidea.springbootplus.framework.ip.service.IpAddressService;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-/**
- * IP地址 服务实现类
- *
- * @author geekidea
- * @since 2020-03-25
- */
-@Slf4j
-@Service
-public class IpAddressServiceImpl extends BaseServiceImpl<IpAddressMapper, IpAddress> implements IpAddressService {
-
-    @Autowired
-    private IpAddressMapper ipAddressMapper;
-
-
-    @Override
-    public IpAddress getByIp(String ip) {
-        if (StringUtils.isBlank(ip)) {
-            return null;
-        }
-        if (CommonConstant.LOCALHOST_IP.equals(ip)) {
-            return new IpAddress().setArea(CommonConstant.LOCALHOST_IP_NAME);
-        }
-        if (CommonConstant.LAN_IP.equals(ip)) {
-            return new IpAddress().setArea(CommonConstant.LAN_IP_NAME);
-        }
-        return ipAddressMapper.getByIp(ip);
-    }
-
-    @Override
-    public String getAreaByIp(String ip) {
-        IpAddress ipAddress = getByIp(ip);
-        if (ipAddress != null) {
-            return ipAddress.getArea();
-        }
-        return null;
-    }
-
-    @Override
-    public String getOperatorByIp(String ip) {
-        IpAddress ipAddress = getByIp(ip);
-        if (ipAddress != null) {
-            return ipAddress.getOperator();
-        }
-        return null;
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/annotation/Module.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/annotation/Module.java
deleted file mode 100644
index 3226119d..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/annotation/Module.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.annotation;
-
-import org.springframework.core.annotation.AliasFor;
-
-import java.lang.annotation.*;
-
-/**
- * 模块名称注解
- *
- * @author geekidea
- * @date 2020/3/19
- **/
-@Target({ElementType.TYPE})
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface Module {
-
-    /**
-     * 模块名称
-     *
-     * @return
-     */
-    String name() default "";
-
-    /**
-     * 模块名称
-     *
-     * @return
-     */
-    @AliasFor("name")
-    String value() default "";
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/annotation/OperationLog.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/annotation/OperationLog.java
deleted file mode 100644
index f90112cc..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/annotation/OperationLog.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.annotation;
-
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-import org.springframework.core.annotation.AliasFor;
-
-import java.lang.annotation.*;
-
-/**
- * 操作日志注解
- * 记录：日志名称，日志类型，日志备注
- *
- * @author geekidea
- * @date 2020/3/19
- **/
-@Target({ElementType.METHOD})
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface OperationLog {
-
-    /**
-     * 日志名称
-     *
-     * @return
-     */
-    String name() default "";
-
-    /**
-     * 日志名称
-     *
-     * @return
-     */
-    @AliasFor("name")
-    String value() default "";
-
-    /**
-     * 日志类型
-     *
-     * @return
-     */
-    OperationLogType type() default OperationLogType.OTHER;
-
-    /**
-     * 日志备注
-     *
-     * @return
-     */
-    String remark() default "";
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/annotation/OperationLogIgnore.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/annotation/OperationLogIgnore.java
deleted file mode 100644
index 1283704b..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/annotation/OperationLogIgnore.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.annotation;
-
-import java.lang.annotation.*;
-
-/**
- * 忽略操作日志记录注解
- * 在controller上标注该方法后，将不会记录操作日志
- * 可以标注在类和方法上，如果标记在类上，则会忽略controller中的所有方法
- *
- * @author geekidea
- * @date 2020/3/19
- **/
-@Target({ElementType.TYPE, ElementType.METHOD})
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-public @interface OperationLogIgnore {
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/aop/BaseLogAop.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/aop/BaseLogAop.java
deleted file mode 100644
index 1b0f38b8..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/aop/BaseLogAop.java
+++ /dev/null
@@ -1,990 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.aop;
-
-import com.alibaba.fastjson.JSONObject;
-import com.auth0.jwt.exceptions.JWTDecodeException;
-import com.baomidou.mybatisplus.core.toolkit.IdWorker;
-import io.geekidea.springbootplus.config.constant.CommonConstant;
-import io.geekidea.springbootplus.config.properties.SpringBootPlusAopProperties;
-import io.geekidea.springbootplus.framework.common.api.ApiCode;
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.common.bean.ClientInfo;
-import io.geekidea.springbootplus.framework.common.exception.SpringBootPlusException;
-import io.geekidea.springbootplus.framework.ip.entity.IpAddress;
-import io.geekidea.springbootplus.framework.ip.service.IpAddressService;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLogIgnore;
-import io.geekidea.springbootplus.framework.log.bean.OperationLogInfo;
-import io.geekidea.springbootplus.framework.log.bean.RequestInfo;
-import io.geekidea.springbootplus.framework.log.entity.SysLoginLog;
-import io.geekidea.springbootplus.framework.log.entity.SysOperationLog;
-import io.geekidea.springbootplus.framework.log.service.SysLoginLogService;
-import io.geekidea.springbootplus.framework.log.service.SysOperationLogService;
-import io.geekidea.springbootplus.framework.shiro.service.LoginToken;
-import io.geekidea.springbootplus.framework.shiro.service.LoginUsername;
-import io.geekidea.springbootplus.framework.shiro.util.JwtTokenUtil;
-import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
-import io.geekidea.springbootplus.framework.util.*;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.MapUtils;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.shiro.authz.annotation.*;
-import org.aspectj.lang.JoinPoint;
-import org.aspectj.lang.ProceedingJoinPoint;
-import org.aspectj.lang.Signature;
-import org.aspectj.lang.reflect.MethodSignature;
-import org.fusesource.jansi.Ansi;
-import org.slf4j.MDC;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.servlet.ModelAndView;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.AnnotatedType;
-import java.lang.reflect.Method;
-import java.util.*;
-
-/**
- * <p>
- * Controller Aop 抽象类
- * 获取响应结果信息
- * <p>
- * 日志输出类型：print-type
- * 1. 请求和响应分开，按照执行顺序打印
- * 2. ThreadLocal线程绑定，方法执行结束时，连续打印请求和响应日志
- * 3. ThreadLocal线程绑定，方法执行结束时，同时打印请求和响应日志
- * </p>
- *
- * @author geekidea
- * @date 2018-11-08
- */
-@Slf4j
-public abstract class BaseLogAop {
-
-    @Autowired
-    protected SysOperationLogService sysOperationLogService;
-
-    @Autowired
-    private IpAddressService ipAddressService;
-
-    @Autowired
-    private SysLoginLogService sysLoginLogService;
-
-    /**
-     * 本地线程变量，保存请求参数信息到当前线程中
-     */
-    protected static ThreadLocal<String> threadLocal = new ThreadLocal<>();
-    protected static ThreadLocal<RequestInfo> requestInfoThreadLocal = new ThreadLocal<>();
-    protected static ThreadLocal<OperationLogInfo> operationLogThreadLocal = new ThreadLocal<>();
-
-    /**
-     * 请求ID
-     */
-    private static final String REQUEST_ID = "requestId";
-    /**
-     * 零
-     */
-    private static final int ZERO = 0;
-    /**
-     * 截取字符串的最多长度
-     */
-    private static final int MAX_LENGTH = 300;
-    /**
-     * 登录日志：登录类型
-     */
-    private static final int LOGIN_TYPE = 1;
-    /**
-     * 登录日志：登出类型
-     */
-    private static final int LOGOUT_TYPE = 2;
-
-    /**
-     * 项目上下文路径
-     */
-    @Value("${server.servlet.context-path}")
-    private String contextPath;
-
-    /**
-     * Aop日志配置
-     */
-    protected SpringBootPlusAopProperties.LogAopConfig logAopConfig;
-
-    /**
-     * 日志打印类型
-     */
-    protected SpringBootPlusAopProperties.LogPrintType logPrintType;
-
-    /**
-     * 是否启用请求ID
-     */
-    protected boolean enableRequestId;
-
-    /**
-     * requestId生成类型
-     */
-    protected SpringBootPlusAopProperties.RequestIdType requestIdType;
-
-    /**
-     * Aop操作日志配置
-     */
-    protected SpringBootPlusAopProperties.OperationLogConfig operationLogConfig;
-
-    /**
-     * Aop登录日志配置
-     */
-    protected SpringBootPlusAopProperties.LoginLogConfig loginLogConfig;
-
-    @Autowired
-    public void setSpringBootPlusAopProperties(SpringBootPlusAopProperties springBootPlusAopProperties) {
-        logAopConfig = springBootPlusAopProperties.getLog();
-        logPrintType = logAopConfig.getLogPrintType();
-        enableRequestId = logAopConfig.isEnableRequestId();
-        requestIdType = logAopConfig.getRequestIdType();
-        operationLogConfig = springBootPlusAopProperties.getOperationLog();
-        loginLogConfig = springBootPlusAopProperties.getLoginLog();
-        log.debug("logAopConfig = " + logAopConfig);
-        log.debug("logPrintType = " + logPrintType);
-        log.debug("enableRequestId = " + enableRequestId);
-        log.debug("requestIdType = " + requestIdType);
-        log.debug("operationLogConfig = " + operationLogConfig);
-        log.debug("loginLogConfig = " + loginLogConfig);
-        log.debug("contextPath = " + contextPath);
-    }
-
-    /**
-     * 环绕通知
-     * 方法执行前打印请求参数信息
-     * 方法执行后打印响应结果信息
-     *
-     * @param joinPoint
-     * @return
-     * @throws Throwable
-     */
-    public abstract Object doAround(ProceedingJoinPoint joinPoint) throws Throwable;
-
-    /**
-     * 异常通知方法
-     *
-     * @param joinPoint
-     * @param exception
-     */
-    public abstract void afterThrowing(JoinPoint joinPoint, Exception exception);
-
-    /**
-     * 设置请求ID
-     *
-     * @param requestInfo
-     */
-    protected abstract void setRequestId(RequestInfo requestInfo);
-
-    /**
-     * 获取请求信息对象
-     *
-     * @param requestInfo
-     */
-    protected abstract void getRequestInfo(RequestInfo requestInfo);
-
-    /**
-     * 获取响应结果对象
-     *
-     * @param apiResult
-     */
-    protected abstract void getResponseResult(Object result);
-
-    /**
-     * 请求响应处理完成之后的回调方法
-     *
-     * @param requestInfo
-     * @param operationLogInfo
-     * @param result
-     * @param exception
-     */
-    protected abstract void finish(RequestInfo requestInfo, OperationLogInfo operationLogInfo, Object result, Exception exception);
-
-    /**
-     * 处理
-     *
-     * @param joinPoint
-     * @return
-     * @throws Throwable
-     */
-    public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {
-        // 获取请求相关信息
-        try {
-            // 获取当前的HttpServletRequest对象
-            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
-            HttpServletRequest request = attributes.getRequest();
-
-            // HTTP请求信息对象
-            RequestInfo requestInfo = new RequestInfo();
-
-            // 请求路径 /api/foobar/add
-            String path = request.getRequestURI();
-            requestInfo.setPath(path);
-            // 获取实际路径 /foobar/add
-            String realPath = getRealPath(path);
-            requestInfo.setRealPath(realPath);
-
-            // 排除路径
-            Set<String> excludePaths = logAopConfig.getExcludePaths();
-            // 请求路径
-            if (handleExcludePaths(excludePaths, realPath)) {
-                return joinPoint.proceed();
-            }
-
-            // 获取请求类名和方法名称
-            Signature signature = joinPoint.getSignature();
-
-            // 获取真实的方法对象
-            MethodSignature methodSignature = (MethodSignature) signature;
-            Method method = methodSignature.getMethod();
-
-            // 处理操作日志信息
-            handleOperationLogInfo(method);
-
-            // IP地址
-            String ip = IpUtil.getRequestIp();
-            requestInfo.setIp(ip);
-
-            // 获取请求方式
-            String requestMethod = request.getMethod();
-            requestInfo.setRequestMethod(requestMethod);
-
-            // 获取请求内容类型
-            String contentType = request.getContentType();
-            requestInfo.setContentType(contentType);
-
-            // 判断控制器方法参数中是否有RequestBody注解
-            Annotation[][] annotations = method.getParameterAnnotations();
-            boolean isRequestBody = isRequestBody(annotations);
-            requestInfo.setRequestBody(isRequestBody);
-
-            AnnotatedType[] annotatedTypes = method.getAnnotatedParameterTypes();
-
-            // 获取Shiro注解值，并记录到map中
-            handleShiroAnnotationValue(requestInfo, method);
-
-            // 设置请求参数
-            Object requestParamObject = getRequestParamObject(joinPoint, request, requestMethod, contentType, isRequestBody);
-            requestInfo.setParam(requestParamObject);
-            requestInfo.setTime(DateUtil.getDateTimeString(new Date()));
-
-            // 获取请求头token
-            String token = request.getHeader(JwtTokenUtil.getTokenName());
-            requestInfo.setToken(token);
-            if (StringUtils.isNotBlank(token)) {
-                requestInfo.setTokenMd5(DigestUtils.md5Hex(token));
-            }
-
-            // 用户浏览器代理字符串
-            requestInfo.setUserAgent(request.getHeader(CommonConstant.USER_AGENT));
-
-            // 记录请求ID
-            setRequestId(requestInfo);
-
-            // 调用子类重写方法，控制请求信息日志处理
-            getRequestInfo(requestInfo);
-        } catch (Exception e) {
-            log.error("请求日志AOP处理异常", e);
-        }
-
-        // 执行目标方法,获得返回值
-        // 方法异常时，会调用子类的@AfterThrowing注解的方法，不会调用下面的代码，异常单独处理
-        Object result = joinPoint.proceed();
-        try {
-            // 调用子类重写方法，控制响应结果日志处理
-            getResponseResult(result);
-        } catch (Exception e) {
-            log.error("处理响应结果异常", e);
-        } finally {
-            handleAfterReturn(result, null);
-        }
-        return result;
-    }
-
-    /**
-     * 正常调用返回或者异常结束后调用此方法
-     *
-     * @param result
-     * @param exception
-     */
-    protected void handleAfterReturn(Object result, Exception exception) {
-        // 获取RequestInfo
-        RequestInfo requestInfo = requestInfoThreadLocal.get();
-        // 获取OperationLogInfo
-        OperationLogInfo operationLogInfo = operationLogThreadLocal.get();
-        // 调用抽象方法，是否保存日志操作，需要子类重写该方法，手动调用saveSysOperationLog
-        finish(requestInfo, operationLogInfo, result, null);
-        // 释放资源
-        remove();
-    }
-
-    /**
-     * 处理异常
-     *
-     * @param exception
-     */
-    public void handleAfterThrowing(Exception exception) {
-        // 获取RequestInfo
-        RequestInfo requestInfo = requestInfoThreadLocal.get();
-        // 获取OperationLogInfo
-        OperationLogInfo operationLogInfo = operationLogThreadLocal.get();
-        // 调用抽象方法，是否保存日志操作，需要子类重写该方法，手动调用saveSysOperationLog
-        finish(requestInfo, operationLogInfo, null, exception);
-        // 释放资源
-        remove();
-    }
-
-
-    private void handleOperationLogInfo(Method method) {
-        // 设置控制器类名称和方法名称
-        OperationLogInfo operationLogInfo = new OperationLogInfo()
-                .setControllerClassName(method.getDeclaringClass().getName())
-                .setControllerMethodName(method.getName());
-
-        // 获取Module类注解
-        Class<?> controllerClass = method.getDeclaringClass();
-        Module module = controllerClass.getAnnotation(Module.class);
-        if (module != null) {
-            String moduleName = module.name();
-            String moduleValue = module.value();
-            if (StringUtils.isNotBlank(moduleValue)) {
-                operationLogInfo.setModule(moduleValue);
-            }
-            if (StringUtils.isNotBlank(moduleName)) {
-                operationLogInfo.setModule(moduleName);
-            }
-        }
-        // 获取OperationLogIgnore注解
-        OperationLogIgnore classOperationLogIgnore = controllerClass.getAnnotation(OperationLogIgnore.class);
-        if (classOperationLogIgnore != null) {
-            // 不记录日志
-            operationLogInfo.setIgnore(true);
-        }
-        // 判断方法是否要过滤
-        OperationLogIgnore operationLogIgnore = method.getAnnotation(OperationLogIgnore.class);
-        if (operationLogIgnore != null) {
-            operationLogInfo.setIgnore(true);
-        }
-        // 从方法上获取OperationLog注解
-        OperationLog operationLog = method.getAnnotation(OperationLog.class);
-        if (operationLog != null) {
-            String operationLogName = operationLog.name();
-            String operationLogValue = operationLog.value();
-            if (StringUtils.isNotBlank(operationLogValue)) {
-                operationLogInfo.setName(operationLogValue);
-            }
-            if (StringUtils.isNotBlank(operationLogName)) {
-                operationLogInfo.setName(operationLogName);
-            }
-            operationLogInfo.setType(operationLog.type().getCode()).setRemark(operationLog.remark());
-        }
-        operationLogThreadLocal.set(operationLogInfo);
-    }
-
-    /**
-     * 获取Shiro注解值，并记录到map中
-     *
-     * @param map
-     * @param method
-     */
-    protected void handleShiroAnnotationValue(RequestInfo requestInfo, Method method) {
-        RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class);
-        if (requiresRoles != null) {
-            String[] requiresRolesValues = requiresRoles.value();
-            if (ArrayUtils.isNotEmpty(requiresRolesValues)) {
-                String requiresRolesString = Arrays.toString(requiresRolesValues);
-                requestInfo.setRequiresRoles(requiresRolesString);
-            }
-        }
-
-        RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
-        if (requiresPermissions != null) {
-            String[] requiresPermissionsValues = requiresPermissions.value();
-            if (ArrayUtils.isNotEmpty(requiresPermissionsValues)) {
-                String requiresPermissionsString = Arrays.toString(requiresPermissionsValues);
-                requestInfo.setRequiresPermissions(requiresPermissionsString);
-            }
-        }
-
-        RequiresAuthentication requiresAuthentication = method.getAnnotation(RequiresAuthentication.class);
-        if (requiresAuthentication != null) {
-            requestInfo.setRequiresAuthentication(true);
-        }
-
-        RequiresUser requiresUser = method.getAnnotation(RequiresUser.class);
-        if (requiresUser != null) {
-            requestInfo.setRequiresUser(true);
-        }
-
-        RequiresGuest requiresGuest = method.getAnnotation(RequiresGuest.class);
-        if (requiresGuest != null) {
-            requestInfo.setRequiresGuest(true);
-        }
-
-    }
-
-    /**
-     * 处理请求ID
-     *
-     * @param requestInfo
-     */
-    protected void handleRequestId(RequestInfo requestInfo) {
-        if (!enableRequestId) {
-            return;
-        }
-        String requestId = null;
-        if (SpringBootPlusAopProperties.RequestIdType.IDWORK == requestIdType) {
-            requestId = IdWorker.getIdStr();
-        } else if (SpringBootPlusAopProperties.RequestIdType.UUID == requestIdType) {
-            requestId = UUIDUtil.getUuid();
-        }
-        // 设置请求ID
-        MDC.put(REQUEST_ID, requestId);
-        requestInfo.setRequestId(requestId);
-    }
-
-    /**
-     * 处理请求参数
-     *
-     * @param requestInfo
-     */
-    protected void handleRequestInfo(RequestInfo requestInfo) {
-        requestInfoThreadLocal.set(requestInfo);
-        if (SpringBootPlusAopProperties.LogPrintType.NONE == logPrintType) {
-            return;
-        }
-        // 获取请求信息
-        String requestInfoString = formatRequestInfo(requestInfo);
-        // 如果打印方式为顺序打印，则直接打印，否则，保存的threadLocal中
-        if (SpringBootPlusAopProperties.LogPrintType.ORDER == logPrintType) {
-            printRequestInfoString(requestInfoString);
-        } else {
-            threadLocal.set(requestInfoString);
-        }
-    }
-
-    /**
-     * 处理响应结果
-     *
-     * @param result
-     */
-    protected void handleResponseResult(Object result) {
-        if (SpringBootPlusAopProperties.LogPrintType.NONE == logPrintType) {
-            return;
-        }
-        if (result != null && result instanceof ApiResult) {
-            ApiResult<?> apiResult = (ApiResult<?>) result;
-            int code = apiResult.getCode();
-            // 获取格式化后的响应结果
-            String responseResultString = formatResponseResult(apiResult);
-            if (SpringBootPlusAopProperties.LogPrintType.ORDER == logPrintType) {
-                printResponseResult(code, responseResultString);
-            } else {
-                // 从threadLocal中获取线程请求信息
-                String requestInfoString = threadLocal.get();
-                // 如果是连续打印，则先打印请求参数，再打印响应结果
-                if (SpringBootPlusAopProperties.LogPrintType.LINE == logPrintType) {
-                    printRequestInfoString(requestInfoString);
-                    printResponseResult(code, responseResultString);
-                } else if (SpringBootPlusAopProperties.LogPrintType.MERGE == logPrintType) {
-                    printRequestResponseString(code, requestInfoString, responseResultString);
-                }
-            }
-        }
-    }
-
-    /**
-     * 同时打印请求和响应信息
-     *
-     * @param code
-     * @param requestInfoString
-     * @param responseResultString
-     */
-    protected void printRequestResponseString(int code, String requestInfoString, String responseResultString) {
-        if (code == ApiCode.SUCCESS.getCode()) {
-            log.info(requestInfoString + "\n" + responseResultString);
-        } else {
-            log.error(requestInfoString + "\n" + responseResultString);
-        }
-    }
-
-
-    /**
-     * 格式化请求信息
-     *
-     * @param requestInfo
-     * @return
-     */
-    protected String formatRequestInfo(RequestInfo requestInfo) {
-        String requestInfoString = null;
-        try {
-            if (logAopConfig.isRequestLogFormat()) {
-                requestInfoString = "\n" + Jackson.toJsonStringNonNull(requestInfo, true);
-            } else {
-                requestInfoString = Jackson.toJsonStringNonNull(requestInfo);
-            }
-        } catch (Exception e) {
-            log.error("格式化请求信息日志异常", e);
-        }
-        return AnsiUtil.getAnsi(Ansi.Color.GREEN, "requestInfo:" + requestInfoString);
-    }
-
-    /**
-     * 打印请求信息
-     *
-     * @param requestInfoString
-     */
-    protected void printRequestInfoString(String requestInfoString) {
-        log.info(requestInfoString);
-    }
-
-    /**
-     * 格式化响应信息
-     *
-     * @param apiResult
-     * @return
-     */
-    protected String formatResponseResult(ApiResult<?> apiResult) {
-        String responseResultString = "responseResult:";
-        try {
-            if (logAopConfig.isResponseLogFormat()) {
-                responseResultString += "\n" + Jackson.toJsonString(apiResult, true);
-            } else {
-                responseResultString += Jackson.toJsonString(apiResult);
-            }
-            int code = apiResult.getCode();
-            if (code == ApiCode.SUCCESS.getCode()) {
-                return AnsiUtil.getAnsi(Ansi.Color.BLUE, responseResultString);
-            } else {
-                return AnsiUtil.getAnsi(Ansi.Color.RED, responseResultString);
-            }
-        } catch (Exception e) {
-            log.error("格式化响应日志异常", e);
-        }
-        return responseResultString;
-    }
-
-    /**
-     * 打印响应信息
-     *
-     * @param code
-     * @param responseResultString
-     */
-    protected void printResponseResult(int code, String responseResultString) {
-        if (code == ApiCode.SUCCESS.getCode()) {
-            log.info(responseResultString);
-        } else {
-            log.error(responseResultString);
-        }
-    }
-
-    /**
-     * 获取请求参数JSON字符串
-     *
-     * @param joinPoint
-     * @param request
-     * @param requestMethod
-     * @param contentType
-     * @param isRequestBody
-     */
-    protected Object getRequestParamObject(ProceedingJoinPoint joinPoint, HttpServletRequest request, String requestMethod, String contentType, boolean isRequestBody) {
-        Object paramObject = null;
-        if (isRequestBody) {
-            // POST,application/json,RequestBody的类型,简单判断,然后序列化成JSON字符串
-            Object[] args = joinPoint.getArgs();
-            paramObject = getArgsObject(args);
-        } else {
-            // 获取getParameterMap中所有的值,处理后序列化成JSON字符串
-            Map<String, String[]> paramsMap = request.getParameterMap();
-            paramObject = getParamJSONObject(paramsMap);
-        }
-        return paramObject;
-    }
-
-    /**
-     * 判断控制器方法参数中是否有RequestBody注解
-     *
-     * @param annotations
-     * @return
-     */
-    protected boolean isRequestBody(Annotation[][] annotations) {
-        boolean isRequestBody = false;
-        for (Annotation[] annotationArray : annotations) {
-            for (Annotation annotation : annotationArray) {
-                if (annotation instanceof RequestBody) {
-                    isRequestBody = true;
-                }
-            }
-        }
-        return isRequestBody;
-    }
-
-    /**
-     * 请求参数拼装
-     *
-     * @param args
-     * @return
-     */
-    protected Object getArgsObject(Object[] args) {
-        if (args == null) {
-            return null;
-        }
-        // 去掉HttpServletRequest和HttpServletResponse
-        List<Object> realArgs = new ArrayList<>();
-        for (Object arg : args) {
-            if (arg instanceof HttpServletRequest) {
-                continue;
-            }
-            if (arg instanceof HttpServletResponse) {
-                continue;
-            }
-            if (arg instanceof MultipartFile) {
-                continue;
-            }
-            if (arg instanceof ModelAndView) {
-                continue;
-            }
-            realArgs.add(arg);
-        }
-        if (realArgs.size() == 1) {
-            return realArgs.get(0);
-        } else {
-            return realArgs;
-        }
-    }
-
-
-    /**
-     * 获取参数Map的JSON字符串
-     *
-     * @param paramsMap
-     * @return
-     */
-    protected JSONObject getParamJSONObject(Map<String, String[]> paramsMap) {
-        if (MapUtils.isEmpty(paramsMap)) {
-            return null;
-        }
-        JSONObject jsonObject = new JSONObject();
-        for (Map.Entry<String, String[]> kv : paramsMap.entrySet()) {
-            String key = kv.getKey();
-            String[] values = kv.getValue();
-            // 没有值
-            if (values == null) {
-                jsonObject.put(key, null);
-            } else if (values.length == 1) {
-                // 一个值
-                jsonObject.put(key, values[0]);
-            } else {
-                // 多个值
-                jsonObject.put(key, values);
-            }
-        }
-        return jsonObject;
-    }
-
-    /**
-     * 处理排除路径，匹配返回true，否则返回false
-     *
-     * @param excludePaths 排除路径
-     * @param realPath     请求实际路径
-     * @return
-     */
-    protected boolean handleExcludePaths(Set<String> excludePaths, String realPath) {
-        if (CollectionUtils.isEmpty(excludePaths) || StringUtils.isBlank(realPath)) {
-            return false;
-        }
-        // 如果是排除路径，则跳过
-        if (excludePaths.contains(realPath)) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * 获取实际路径
-     *
-     * @param requestPath
-     * @return
-     */
-    private String getRealPath(String requestPath) {
-        // 如果项目路径不为空，则去掉项目路径，获取实际访问路径
-        if (StringUtils.isNotBlank(contextPath)) {
-            return requestPath.substring(contextPath.length());
-        }
-        return requestPath;
-    }
-
-    /**
-     * 异步保存系统操作日志
-     *
-     * @param requestInfo
-     * @param operationLogInfo
-     * @param result
-     * @param exception
-     */
-    @Async
-    protected void saveSysOperationLog(RequestInfo requestInfo, OperationLogInfo operationLogInfo, Object result, Exception exception) {
-        try {
-            // 如果不记录操作日志，则跳过
-            if (!operationLogConfig.isEnable()) {
-                return;
-            }
-            // 排除路径
-            Set<String> excludePaths = operationLogConfig.getExcludePaths();
-            // 请求路径
-            if (handleExcludePaths(excludePaths, requestInfo.getRealPath())) {
-                return;
-            }
-
-            // 操作日志
-            SysOperationLog sysOperationLog = new SysOperationLog();
-            // 设置操作日志信息
-            if (operationLogInfo != null) {
-                // 如果类或方法上标注有OperationLogIgnore，则跳过
-                if (operationLogInfo.isIgnore()) {
-                    return;
-                }
-                sysOperationLog.setModule(operationLogInfo.getModule())
-                        .setName(operationLogInfo.getName())
-                        .setType(operationLogInfo.getType())
-                        .setRemark(operationLogInfo.getRemark())
-                        .setClassName(operationLogInfo.getControllerClassName())
-                        .setMethodName(operationLogInfo.getControllerMethodName());
-            }
-            // 设置请求参数信息
-            if (requestInfo != null) {
-                sysOperationLog.setIp(requestInfo.getIp())
-                        .setPath(requestInfo.getPath())
-                        .setRequestId(requestInfo.getRequestId())
-                        .setRequestMethod(requestInfo.getRequestMethod())
-                        .setContentType(requestInfo.getContentType())
-                        .setRequestBody(requestInfo.getRequestBody())
-                        .setToken(requestInfo.getTokenMd5());
-
-                // 设置参数字符串
-                sysOperationLog.setParam(Jackson.toJsonStringNonNull(requestInfo.getParam()));
-                // User-Agent
-                ClientInfo clientInfo = ClientInfoUtil.get(requestInfo.getUserAgent());
-                if (clientInfo != null) {
-                    sysOperationLog.setBrowserName(clientInfo.getBrowserName())
-                            .setBrowserVersion(clientInfo.getBrowserversion())
-                            .setEngineName(clientInfo.getEngineName())
-                            .setEngineVersion(clientInfo.getEngineVersion())
-                            .setOsName(clientInfo.getOsName())
-                            .setPlatformName(clientInfo.getPlatformName())
-                            .setMobile(clientInfo.isMobile())
-                            .setDeviceName(clientInfo.getDeviceName())
-                            .setDeviceModel(clientInfo.getDeviceModel());
-                }
-                // 设置IP区域
-                IpAddress ipAddress = ipAddressService.getByIp(requestInfo.getIp());
-                if (ipAddress != null) {
-                    requestInfo.setIpAddress(ipAddress);
-                    sysOperationLog.setArea(ipAddress.getArea()).setOperator(ipAddress.getOperator());
-                }
-            }
-
-            // 设置响应结果
-            if (result != null && result instanceof ApiResult) {
-                ApiResult<?> apiResult = (ApiResult<?>) result;
-                apiResult.getCode();
-                sysOperationLog.setSuccess(apiResult.isSuccess())
-                        .setCode(apiResult.getCode())
-                        .setMessage(apiResult.getMessage());
-            }
-
-            // 设置当前登录信息
-            sysOperationLog.setUserId(LoginUtil.getUserId()).setUserName(LoginUtil.getUsername());
-
-            // 设置异常信息
-            if (exception != null) {
-                Integer errorCode = null;
-                String exceptionMessage = exception.getMessage();
-                if (StringUtils.isNotBlank(exceptionMessage)) {
-                    exceptionMessage = StringUtils.substring(exceptionMessage, ZERO, MAX_LENGTH);
-                }
-                if (exception instanceof SpringBootPlusException) {
-                    SpringBootPlusException springBootPlusException = (SpringBootPlusException) exception;
-                    errorCode = springBootPlusException.getErrorCode();
-                }
-                // 异常字符串长度截取
-                sysOperationLog.setSuccess(false)
-                        .setCode(errorCode)
-                        .setExceptionMessage(exceptionMessage)
-                        .setExceptionName(exception.getClass().getName());
-            }
-            // 保存日志到数据库
-            sysOperationLogService.saveSysOperationLog(sysOperationLog);
-
-
-        } catch (Exception e) {
-            if (e instanceof JWTDecodeException) {
-                JWTDecodeException jwtDecodeException = (JWTDecodeException) e;
-                throw jwtDecodeException;
-            }
-            log.error("保存系统操作日志失败", e);
-        }
-    }
-
-    /**
-     * 异步保存系统登录日志
-     *
-     * @param requestInfo
-     * @param operationLogInfo
-     * @param result
-     * @param exception
-     */
-    @Async
-    protected void saveSysLoginLog(RequestInfo requestInfo, OperationLogInfo operationLogInfo, Object result, Exception exception) {
-        try {
-            // 如果不记录登录日志，则跳过
-            if (!loginLogConfig.isEnable()) {
-                return;
-            }
-            String realPath = requestInfo.getRealPath();
-            if (StringUtils.isBlank(realPath)) {
-                return;
-            }
-
-            boolean flag = false;
-            Integer type = null;
-            // 判断是否是登录路径
-            if (realPath.equals(loginLogConfig.getLoginPath())) {
-                flag = true;
-                type = LOGIN_TYPE;
-            } else if (realPath.equals(loginLogConfig.getLogoutPath())) {
-                flag = true;
-                type = LOGOUT_TYPE;
-            }
-
-            // 保存登录登出日志
-            if (flag) {
-                SysLoginLog sysLoginLog = new SysLoginLog();
-                sysLoginLog.setType(type);
-                // 设置异常信息
-                if (exception != null) {
-                    Integer errorCode = null;
-                    String exceptionMessage = exception.getMessage();
-                    if (StringUtils.isNotBlank(exceptionMessage)) {
-                        exceptionMessage = StringUtils.substring(exceptionMessage, ZERO, MAX_LENGTH);
-                    }
-                    if (exception instanceof SpringBootPlusException) {
-                        SpringBootPlusException springBootPlusException = (SpringBootPlusException) exception;
-                        errorCode = springBootPlusException.getErrorCode();
-                    }
-                    // 异常字符串长度截取
-                    sysLoginLog.setCode(errorCode).setExceptionMessage(exceptionMessage);
-                }
-
-                // 判断登录登出结果
-                if (result != null && result instanceof ApiResult) {
-                    ApiResult<?> apiResult = (ApiResult<?>) result;
-                    sysLoginLog.setSuccess(apiResult.isSuccess()).setCode(apiResult.getCode());
-                    if (apiResult.isSuccess()) {
-                        if (LOGIN_TYPE == type) {
-                            Object object = apiResult.getData();
-                            if (object != null && object instanceof LoginToken) {
-                                LoginToken loginToken = (LoginToken) object;
-                                String token = loginToken.getToken();
-                                if (StringUtils.isNotBlank(token)) {
-                                    // 设置登录token
-                                    String tokenMd5 = DigestUtils.md5Hex(token);
-                                    sysLoginLog.setToken(tokenMd5);
-                                }
-                            }
-                        }
-                    } else {
-                        sysLoginLog.setExceptionMessage(apiResult.getMessage());
-                    }
-                }
-
-                // 设置请求参数信息
-                if (requestInfo != null) {
-                    sysLoginLog.setIp(requestInfo.getIp()).setRequestId(requestInfo.getRequestId());
-                    // 设置登录用户名
-                    if (LOGIN_TYPE == type) {
-                        Object paramObject = requestInfo.getParam();
-                        if (paramObject != null && paramObject instanceof LoginUsername) {
-                            LoginUsername loginUsername = (LoginUsername) paramObject;
-                            String username = loginUsername.getUsername();
-                            sysLoginLog.setUsername(username);
-                        }
-                    } else if (LOGOUT_TYPE == type) {
-                        String username = JwtUtil.getUsername(requestInfo.getToken());
-                        sysLoginLog.setUsername(username);
-                        // 设置登出token
-                        sysLoginLog.setToken(requestInfo.getTokenMd5());
-                    }
-
-                    // User-Agent
-                    String userAgent = requestInfo.getUserAgent();
-                    if (StringUtils.isNotBlank(userAgent)) {
-                        sysLoginLog.setUserAgent(StringUtils.substring(userAgent, ZERO, MAX_LENGTH));
-                    }
-                    ClientInfo clientInfo = ClientInfoUtil.get(userAgent);
-                    if (clientInfo != null) {
-                        sysLoginLog.setBrowserName(clientInfo.getBrowserName())
-                                .setBrowserVersion(clientInfo.getBrowserversion())
-                                .setEngineName(clientInfo.getEngineName())
-                                .setEngineVersion(clientInfo.getEngineVersion())
-                                .setOsName(clientInfo.getOsName())
-                                .setPlatformName(clientInfo.getPlatformName())
-                                .setMobile(clientInfo.isMobile())
-                                .setDeviceName(clientInfo.getDeviceName())
-                                .setDeviceModel(clientInfo.getDeviceModel());
-                    }
-                    IpAddress ipAddress = requestInfo.getIpAddress();
-                    if (ipAddress == null) {
-                        ipAddress = ipAddressService.getByIp(requestInfo.getIp());
-                    }
-                    if (ipAddress != null) {
-                        sysLoginLog.setArea(ipAddress.getArea()).setOperator(ipAddress.getOperator());
-                    }
-                    // 保存登录日志
-                    sysLoginLogService.saveSysLoginLog(sysLoginLog);
-                }
-            }
-        } catch (Exception e) {
-            log.error("保存系统登录日志失败", e);
-        }
-    }
-
-
-    /**
-     * 释放资源
-     */
-    protected void remove() {
-        threadLocal.remove();
-        requestInfoThreadLocal.remove();
-        operationLogThreadLocal.remove();
-        MDC.clear();
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/bean/OperationLogInfo.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/bean/OperationLogInfo.java
deleted file mode 100644
index 4841a37d..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/bean/OperationLogInfo.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.bean;
-
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-/**
- * 操作日志信息
- *
- * @author geekidea
- * @date 2020/3/19
- **/
-@Data
-@Accessors(chain = true)
-public class OperationLogInfo {
-
-    /**
-     * 是否忽略
-     */
-    private boolean ignore;
-
-    /**
-     * 模块名称
-     */
-    private String module;
-
-    /**
-     * 日志名称
-     */
-    private String name;
-
-    /**
-     * 日志类型
-     */
-    private Integer type;
-
-    /**
-     * 日志备注
-     */
-    private String remark;
-
-    /**
-     * controller类名称
-     */
-    private String controllerClassName;
-
-    /**
-     * controller目标方法名称
-     */
-    private String controllerMethodName;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/bean/RequestInfo.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/bean/RequestInfo.java
deleted file mode 100644
index aa035f05..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/bean/RequestInfo.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.bean;
-
-import com.alibaba.fastjson.annotation.JSONField;
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import io.geekidea.springbootplus.framework.ip.entity.IpAddress;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-
-/**
- * HTTP请求信息对象
- *
- * @author geekidea
- * @date 2020/3/18
- **/
-@Data
-@Accessors(chain = true)
-public class RequestInfo implements Serializable {
-    private static final long serialVersionUID = 1421424612944015973L;
-
-    /**
-     * 请求路径
-     * /api/foobar/add
-     */
-    private String path;
-
-    /**
-     * 请求ID
-     */
-    @JsonIgnore
-    @JSONField(serialize = false)
-    private String requestId;
-
-    /**
-     * 请求实际路径
-     * /foobar/add
-     */
-    @JsonIgnore
-    @JSONField(serialize = false)
-    private String realPath;
-
-    /**
-     * 请求IP地址
-     */
-    private String ip;
-
-    /**
-     * 请求IP对象
-     */
-    @JsonIgnore
-    @JSONField(serialize = false)
-    private IpAddress ipAddress;
-
-    /**
-     * 请求方式，GET/POST
-     */
-    private String requestMethod;
-
-    /**
-     * 请求内容类型
-     */
-    private String contentType;
-
-    /**
-     * 判断控制器方法参数中是否有RequestBody注解
-     */
-    private Boolean requestBody;
-
-    /**
-     * 请求参数对象
-     */
-    private Object param;
-
-    /**
-     * 请求时间字符串
-     */
-    private String time;
-
-    /**
-     * 请求token
-     */
-    private String token;
-
-    /**
-     * 请求token MD5值
-     */
-    @JsonIgnore
-    @JSONField(serialize = false)
-    private String tokenMd5;
-
-    /**
-     * 用户代理字符串
-     */
-    @JsonIgnore
-    @JSONField(serialize = false)
-    private String userAgent;
-
-    /**
-     * requiresRoles值
-     */
-    private String requiresRoles;
-
-    /**
-     * requiresPermissions值
-     */
-    private String requiresPermissions;
-
-    /**
-     * requiresAuthentication
-     */
-    private Boolean requiresAuthentication;
-
-    /**
-     * requiresUser
-     */
-    private Boolean requiresUser;
-
-    /**
-     * requiresGuest
-     */
-    private Boolean requiresGuest;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/controller/SysLoginLogController.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/controller/SysLoginLogController.java
deleted file mode 100644
index f4e4e66f..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/controller/SysLoginLogController.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.controller;
-
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.common.controller.BaseController;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.entity.SysLoginLog;
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-import io.geekidea.springbootplus.framework.log.param.SysLoginLogPageParam;
-import io.geekidea.springbootplus.framework.log.service.SysLoginLogService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.validation.annotation.Validated;
-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;
-
-/**
- * 系统登录日志 控制器
- *
- * @author geekidea
- * @since 2020-03-24
- */
-@Slf4j
-@RestController
-@RequestMapping("/sysLoginLog")
-@Module("log")
-@Api(value = "系统登录日志API", tags = {"系统登录日志"})
-public class SysLoginLogController extends BaseController {
-
-    @Autowired
-    private SysLoginLogService sysLoginLogService;
-
-    /**
-     * 系统登录日志分页列表
-     */
-    @PostMapping("/getPageList")
-    @RequiresPermissions("sys:login:log:page")
-    @OperationLog(name = "系统登录日志分页列表", type = OperationLogType.PAGE)
-    @ApiOperation(value = "系统登录日志分页列表", response = SysLoginLog.class)
-    public ApiResult<Paging<SysLoginLog>> getSysLoginLogPageList(@Validated @RequestBody SysLoginLogPageParam sysLoginLogPageParam) throws Exception {
-        Paging<SysLoginLog> paging = sysLoginLogService.getSysLoginLogPageList(sysLoginLogPageParam);
-        return ApiResult.ok(paging);
-    }
-
-}
-
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/controller/SysOperationLogController.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/controller/SysOperationLogController.java
deleted file mode 100644
index 9c6db9ed..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/controller/SysOperationLogController.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.controller;
-
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.common.controller.BaseController;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.log.entity.SysOperationLog;
-import io.geekidea.springbootplus.framework.log.param.SysOperationLogPageParam;
-import io.geekidea.springbootplus.framework.log.service.SysOperationLogService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.validation.annotation.Validated;
-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;
-
-/**
- * 系统操作日志 控制器
- *
- * @author geekidea
- * @since 2020-03-19
- */
-@Slf4j
-@RestController
-@RequestMapping("/sysOperationLog")
-@Api(value = "系统操作日志API", tags = {"系统操作日志"})
-public class SysOperationLogController extends BaseController {
-
-    @Autowired
-    private SysOperationLogService sysOperationLogService;
-
-    /**
-     * 系统操作日志分页列表
-     */
-    @PostMapping("/getPageList")
-    @RequiresPermissions("sys:operation:log:page")
-    @ApiOperation(value = "系统操作日志分页列表", response = SysOperationLog.class)
-    public ApiResult<Paging<SysOperationLog>> getSysOperationLogPageList(@Validated @RequestBody SysOperationLogPageParam sysOperationLogPageParam) throws Exception {
-        Paging<SysOperationLog> paging = sysOperationLogService.getSysOperationLogPageList(sysOperationLogPageParam);
-        return ApiResult.ok(paging);
-    }
-
-}
-
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/entity/SysLoginLog.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/entity/SysLoginLog.java
deleted file mode 100644
index 25ac5546..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/entity/SysLoginLog.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
-import io.geekidea.springbootplus.framework.core.validator.groups.Update;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-import javax.validation.constraints.NotNull;
-import java.util.Date;
-
-/**
- * 系统登录日志
- *
- * @author geekidea
- * @since 2020-03-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "SysLoginLog对象")
-public class SysLoginLog extends BaseEntity {
-    private static final long serialVersionUID = 1L;
-
-    @NotNull(message = "id不能为空", groups = {Update.class})
-    @ApiModelProperty("主键")
-    @TableId(value = "id", type = IdType.AUTO)
-    private Long id;
-
-    @ApiModelProperty("请求ID")
-    private String requestId;
-
-    @ApiModelProperty("用户名称")
-    private String username;
-
-    @ApiModelProperty("IP")
-    private String ip;
-
-    @ApiModelProperty("区域")
-    private String area;
-
-    @ApiModelProperty("运营商")
-    private String operator;
-
-    @ApiModelProperty("tokenMd5值")
-    private String token;
-
-    @ApiModelProperty("1:登录，2：登出")
-    private Integer type;
-
-    @ApiModelProperty("是否成功 true:成功/false:失败")
-    private Boolean success;
-
-    @ApiModelProperty("响应码")
-    private Integer code;
-
-    @ApiModelProperty("失败消息记录")
-    private String exceptionMessage;
-
-    @ApiModelProperty("浏览器名称")
-    private String userAgent;
-
-    @ApiModelProperty("浏览器名称")
-    private String browserName;
-
-    @ApiModelProperty("浏览器版本")
-    private String browserVersion;
-
-    @ApiModelProperty("浏览器引擎名称")
-    private String engineName;
-
-    @ApiModelProperty("浏览器引擎版本")
-    private String engineVersion;
-
-    @ApiModelProperty("系统名称")
-    private String osName;
-
-    @ApiModelProperty("平台名称")
-    private String platformName;
-
-    @ApiModelProperty("是否是手机,0:否,1:是")
-    private Boolean mobile;
-
-    @ApiModelProperty("移动端设备名称")
-    private String deviceName;
-
-    @ApiModelProperty("移动端设备型号")
-    private String deviceModel;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("创建时间")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    private Date updateTime;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/entity/SysOperationLog.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/entity/SysOperationLog.java
deleted file mode 100644
index 87a7b602..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/entity/SysOperationLog.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
-import io.geekidea.springbootplus.framework.core.validator.groups.Update;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-import javax.validation.constraints.NotNull;
-import java.util.Date;
-
-/**
- * 系统操作日志
- *
- * @author geekidea
- * @since 2020-03-19
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "SysOperationLog对象")
-public class SysOperationLog extends BaseEntity {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty("主键")
-    @TableId(value = "id", type = IdType.AUTO)
-    @NotNull(message = "id不能为空", groups = {Update.class})
-    private Long id;
-
-    @ApiModelProperty("请求ID")
-    private String requestId;
-
-    @ApiModelProperty("用户ID")
-    private Long userId;
-
-    @ApiModelProperty("用户名称")
-    private String userName;
-
-    @ApiModelProperty("日志名称")
-    private String name;
-
-    @ApiModelProperty("IP")
-    private String ip;
-
-    @ApiModelProperty("区域")
-    private String area;
-
-    @ApiModelProperty("运营商")
-    private String operator;
-
-    @ApiModelProperty("全路径")
-    private String path;
-
-    @ApiModelProperty("模块名称")
-    private String module;
-
-    @ApiModelProperty("类名")
-    private String className;
-
-    @ApiModelProperty("方法名称")
-    private String methodName;
-
-    @ApiModelProperty("请求方式，GET/POST")
-    private String requestMethod;
-
-    @ApiModelProperty("内容类型")
-    private String contentType;
-
-    @ApiModelProperty("是否是JSON请求映射参数")
-    private Boolean requestBody;
-
-    @ApiModelProperty("请求参数")
-    private String param;
-
-    @ApiModelProperty("tokenMd5值")
-    private String token;
-
-    @ApiModelProperty("0:其它,1:新增,2:修改,3:删除,4:详情查询,5:所有列表,6:分页列表,7:其它查询,8:上传文件")
-    private Integer type;
-
-    @ApiModelProperty("0:失败,1:成功")
-    private Boolean success;
-
-    @ApiModelProperty("响应结果状态码")
-    private Integer code;
-
-    @ApiModelProperty("响应结果消息")
-    private String message;
-
-    @ApiModelProperty("异常类名称")
-    private String exceptionName;
-
-    @ApiModelProperty("异常信息")
-    private String exceptionMessage;
-
-    @ApiModelProperty("浏览器名称")
-    private String browserName;
-
-    @ApiModelProperty("浏览器版本")
-    private String browserVersion;
-
-    @ApiModelProperty("浏览器引擎名称")
-    private String engineName;
-
-    @ApiModelProperty("浏览器引擎版本")
-    private String engineVersion;
-
-    @ApiModelProperty("系统名称")
-    private String osName;
-
-    @ApiModelProperty("平台名称")
-    private String platformName;
-
-    @ApiModelProperty("是否是手机,0:否,1:是")
-    private Boolean mobile;
-
-    @ApiModelProperty("移动端设备名称")
-    private String deviceName;
-
-    @ApiModelProperty("移动端设备型号")
-    private String deviceModel;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("创建时间")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    private Date updateTime;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/enums/OperationLogType.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/enums/OperationLogType.java
deleted file mode 100644
index cdd14198..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/enums/OperationLogType.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.enums;
-
-import io.geekidea.springbootplus.framework.common.enums.BaseEnum;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * 操作日志类型枚举
- *
- * @author geekidea
- * @date 2020/3/19
- **/
-@Getter
-@AllArgsConstructor
-public enum OperationLogType implements BaseEnum {
-    /**
-     * 其它
-     **/
-    OTHER(0, "其它"),
-    /**
-     * 添加
-     **/
-    ADD(1, "添加"),
-    /**
-     * 修改
-     **/
-    UPDATE(2, "修改"),
-    /**
-     * 删除
-     **/
-    DELETE(3, "删除"),
-    /**
-     * 查询
-     **/
-    query(4, "详情查询"),
-    /**
-     * 详情查询
-     **/
-    INFO(5, "详情查询"),
-    /**
-     * 列表查询
-     **/
-    LIST(6, "列表查询"),
-    /**
-     * 分页列表
-     **/
-    PAGE(7, "分页列表"),
-    /**
-     * 其它查询
-     **/
-    OTHER_QUERY(8, "其它查询"),
-    /**
-     * 文件上传
-     **/
-    UPLOAD(9, "文件上传"),
-    /**
-     * 文件下载
-     **/
-    download(10, "文件下载"),
-    /**
-     * Excel导入
-     **/
-    excel_import(11, "Excel导入"),
-    /**
-     * Excel导出
-     **/
-    EXCEL_EXPORT(12, "Excel导出");
-
-    private Integer code;
-    private String desc;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/mapper/SysLoginLogMapper.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/mapper/SysLoginLogMapper.java
deleted file mode 100644
index 1ca535fb..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/mapper/SysLoginLogMapper.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import io.geekidea.springbootplus.framework.log.entity.SysLoginLog;
-import org.springframework.stereotype.Repository;
-
-/**
- * 系统登录日志 Mapper 接口
- *
- * @author geekidea
- * @since 2020-03-24
- */
-@Repository
-public interface SysLoginLogMapper extends BaseMapper<SysLoginLog> {
-
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/mapper/SysOperationLogMapper.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/mapper/SysOperationLogMapper.java
deleted file mode 100644
index 431aef35..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/mapper/SysOperationLogMapper.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import io.geekidea.springbootplus.framework.log.entity.SysOperationLog;
-import org.springframework.stereotype.Repository;
-
-/**
- * 系统操作日志 Mapper 接口
- *
- * @author geekidea
- * @since 2020-03-19
- */
-@Repository
-public interface SysOperationLogMapper extends BaseMapper<SysOperationLog> {
-
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/param/SysLoginLogPageParam.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/param/SysLoginLogPageParam.java
deleted file mode 100644
index c44e8624..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/param/SysLoginLogPageParam.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.param;
-
-import io.geekidea.springbootplus.framework.core.pagination.BasePageOrderParam;
-import io.swagger.annotations.ApiModel;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-/**
- * <pre>
- * 系统登录日志 分页参数对象
- * </pre>
- *
- * @author geekidea
- * @date 2020-03-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "系统登录日志分页参数")
-public class SysLoginLogPageParam extends BasePageOrderParam {
-    private static final long serialVersionUID = 1L;
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/param/SysOperationLogPageParam.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/param/SysOperationLogPageParam.java
deleted file mode 100644
index cfb94209..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/param/SysOperationLogPageParam.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.param;
-
-import io.geekidea.springbootplus.framework.core.pagination.BasePageOrderParam;
-import io.swagger.annotations.ApiModel;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-/**
- * <pre>
- * 系统操作日志 分页参数对象
- * </pre>
- *
- * @author geekidea
- * @date 2020-03-19
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "系统操作日志分页参数")
-public class SysOperationLogPageParam extends BasePageOrderParam {
-    private static final long serialVersionUID = 1L;
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/service/SysLoginLogService.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/service/SysLoginLogService.java
deleted file mode 100644
index 1746e32a..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/service/SysLoginLogService.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.service;
-
-import io.geekidea.springbootplus.framework.common.service.BaseService;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.log.entity.SysLoginLog;
-import io.geekidea.springbootplus.framework.log.param.SysLoginLogPageParam;
-
-/**
- * 系统登录日志 服务类
- *
- * @author geekidea
- * @since 2020-03-24
- */
-public interface SysLoginLogService extends BaseService<SysLoginLog> {
-
-    /**
-     * 保存
-     *
-     * @param sysLoginLog
-     * @return
-     * @throws Exception
-     */
-    boolean saveSysLoginLog(SysLoginLog sysLoginLog) throws Exception;
-
-    /**
-     * 修改
-     *
-     * @param sysLoginLog
-     * @return
-     * @throws Exception
-     */
-    boolean updateSysLoginLog(SysLoginLog sysLoginLog) throws Exception;
-
-    /**
-     * 删除
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    boolean deleteSysLoginLog(Long id) throws Exception;
-
-
-    /**
-     * 获取分页对象
-     *
-     * @param sysLoginLogQueryParam
-     * @return
-     * @throws Exception
-     */
-    Paging<SysLoginLog> getSysLoginLogPageList(SysLoginLogPageParam sysLoginLogPageParam) throws Exception;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/service/SysOperationLogService.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/service/SysOperationLogService.java
deleted file mode 100644
index 1e11d710..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/service/SysOperationLogService.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.service;
-
-import io.geekidea.springbootplus.framework.common.service.BaseService;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.log.entity.SysOperationLog;
-import io.geekidea.springbootplus.framework.log.param.SysOperationLogPageParam;
-
-/**
- * 系统操作日志 服务类
- *
- * @author geekidea
- * @since 2020-03-19
- */
-public interface SysOperationLogService extends BaseService<SysOperationLog> {
-
-    /**
-     * 保存
-     *
-     * @param sysOperationLog
-     * @return
-     * @throws Exception
-     */
-    boolean saveSysOperationLog(SysOperationLog sysOperationLog) throws Exception;
-
-    /**
-     * 修改
-     *
-     * @param sysOperationLog
-     * @return
-     * @throws Exception
-     */
-    boolean updateSysOperationLog(SysOperationLog sysOperationLog) throws Exception;
-
-    /**
-     * 删除
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    boolean deleteSysOperationLog(Long id) throws Exception;
-
-
-    /**
-     * 获取分页对象
-     *
-     * @param sysOperationLogQueryParam
-     * @return
-     * @throws Exception
-     */
-    Paging<SysOperationLog> getSysOperationLogPageList(SysOperationLogPageParam sysOperationLogPageParam) throws Exception;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/service/impl/SysLoginLogServiceImpl.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/service/impl/SysLoginLogServiceImpl.java
deleted file mode 100644
index 8d52c069..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/service/impl/SysLoginLogServiceImpl.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
-import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.log.entity.SysLoginLog;
-import io.geekidea.springbootplus.framework.log.mapper.SysLoginLogMapper;
-import io.geekidea.springbootplus.framework.log.param.SysLoginLogPageParam;
-import io.geekidea.springbootplus.framework.log.service.SysLoginLogService;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-/**
- * 系统登录日志 服务实现类
- *
- * @author geekidea
- * @since 2020-03-24
- */
-@Slf4j
-@Service
-public class SysLoginLogServiceImpl extends BaseServiceImpl<SysLoginLogMapper, SysLoginLog> implements SysLoginLogService {
-
-    @Autowired
-    private SysLoginLogMapper sysLoginLogMapper;
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean saveSysLoginLog(SysLoginLog sysLoginLog) throws Exception {
-        return super.save(sysLoginLog);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean updateSysLoginLog(SysLoginLog sysLoginLog) throws Exception {
-        return super.updateById(sysLoginLog);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean deleteSysLoginLog(Long id) throws Exception {
-        return super.removeById(id);
-    }
-
-    @Override
-    public Paging<SysLoginLog> getSysLoginLogPageList(SysLoginLogPageParam sysLoginLogPageParam) throws Exception {
-        Page<SysLoginLog> page = new PageInfo<>(sysLoginLogPageParam,OrderItem.desc(getLambdaColumn(SysLoginLog::getCreateTime)));
-        LambdaQueryWrapper<SysLoginLog> wrapper = new LambdaQueryWrapper<>();
-        IPage<SysLoginLog> iPage = sysLoginLogMapper.selectPage(page, wrapper);
-        return new Paging<SysLoginLog>(iPage);
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/log/service/impl/SysOperationLogServiceImpl.java b/framework/src/main/java/io/geekidea/springbootplus/framework/log/service/impl/SysOperationLogServiceImpl.java
deleted file mode 100644
index 291f774b..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/log/service/impl/SysOperationLogServiceImpl.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.log.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
-import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.log.entity.SysOperationLog;
-import io.geekidea.springbootplus.framework.log.mapper.SysOperationLogMapper;
-import io.geekidea.springbootplus.framework.log.param.SysOperationLogPageParam;
-import io.geekidea.springbootplus.framework.log.service.SysOperationLogService;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-/**
- * 系统操作日志 服务实现类
- *
- * @author geekidea
- * @since 2020-03-19
- */
-@Slf4j
-@Service
-public class SysOperationLogServiceImpl extends BaseServiceImpl<SysOperationLogMapper, SysOperationLog> implements SysOperationLogService {
-
-    @Autowired
-    private SysOperationLogMapper sysOperationLogMapper;
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean saveSysOperationLog(SysOperationLog sysOperationLog) throws Exception {
-        return super.save(sysOperationLog);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean updateSysOperationLog(SysOperationLog sysOperationLog) throws Exception {
-        return super.updateById(sysOperationLog);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean deleteSysOperationLog(Long id) throws Exception {
-        return super.removeById(id);
-    }
-
-    @Override
-    public Paging<SysOperationLog> getSysOperationLogPageList(SysOperationLogPageParam sysOperationLogPageParam) throws Exception {
-        Page<SysOperationLog> page = new PageInfo<>(sysOperationLogPageParam,OrderItem.desc(getLambdaColumn(SysOperationLog::getCreateTime)));
-        LambdaQueryWrapper<SysOperationLog> wrapper = new LambdaQueryWrapper<>();
-        IPage<SysOperationLog> iPage = sysOperationLogMapper.selectPage(page, wrapper);
-        return new Paging<SysOperationLog>(iPage);
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/cache/LoginRedisService.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/cache/LoginRedisService.java
deleted file mode 100644
index 2a7d7dc9..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/cache/LoginRedisService.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.cache;
-
-
-import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
-import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserRedisVo;
-import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
-
-/**
- * 登录信息Redis缓存操作服务
- *
- * @author geekidea
- * @date 2019-09-30
- * @since 1.3.0.RELEASE
- **/
-public interface LoginRedisService {
-
-    /**
-     * 缓存登录信息
-     *
-     * @param jwtToken
-     * @param loginSysUserVo
-     */
-    void cacheLoginInfo(JwtToken jwtToken, LoginSysUserVo loginSysUserVo);
-
-
-    /**
-     * 刷新登录信息
-     *
-     * @param oldToken
-     * @param username
-     * @param newJwtToken
-     */
-    void refreshLoginInfo(String oldToken, String username, JwtToken newJwtToken);
-
-    /**
-     * 通过用户名，从缓存中获取登录用户LoginSysUserRedisVo
-     *
-     * @param username
-     * @return
-     */
-    LoginSysUserRedisVo getLoginSysUserRedisVo(String username);
-
-    /**
-     * 获取登录用户对象
-     *
-     * @param username
-     * @return
-     */
-    LoginSysUserVo getLoginSysUserVo(String username);
-
-    /**
-     * 通过用户名称获取盐值
-     *
-     * @param username
-     * @return
-     */
-    String getSalt(String username);
-
-    /**
-     * 删除对应用户的Redis缓存
-     *
-     * @param token
-     * @param username
-     */
-    void deleteLoginInfo(String token, String username);
-
-    /**
-     * 判断token在redis中是否存在
-     *
-     * @param token
-     * @return
-     */
-    boolean exists(String token);
-
-    /**
-     * 删除用户所有登录缓存
-     *
-     * @param username
-     */
-    void deleteUserAllCache(String username);
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/cache/impl/LoginRedisServiceImpl.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/cache/impl/LoginRedisServiceImpl.java
deleted file mode 100644
index 011c7cbb..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/cache/impl/LoginRedisServiceImpl.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.cache.impl;
-
-import io.geekidea.springbootplus.config.constant.CommonRedisKey;
-import io.geekidea.springbootplus.config.properties.JwtProperties;
-import io.geekidea.springbootplus.framework.common.bean.ClientInfo;
-import io.geekidea.springbootplus.framework.shiro.cache.LoginRedisService;
-import io.geekidea.springbootplus.framework.shiro.convert.LoginSysUserVoConvert;
-import io.geekidea.springbootplus.framework.shiro.convert.ShiroMapstructConvert;
-import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
-import io.geekidea.springbootplus.framework.shiro.vo.JwtTokenRedisVo;
-import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserRedisVo;
-import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
-import io.geekidea.springbootplus.framework.util.ClientInfoUtil;
-import io.geekidea.springbootplus.framework.util.HttpServletRequestUtil;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.stereotype.Service;
-
-import java.time.Duration;
-import java.util.List;
-import java.util.Set;
-
-/**
- * 登录信息Redis缓存服务类
- *
- * @author geekidea
- * @date 2019-09-30
- * @since 1.3.0.RELEASE
- **/
-@Service
-public class LoginRedisServiceImpl implements LoginRedisService {
-
-    @Autowired
-    private JwtProperties jwtProperties;
-
-    @Autowired
-    private RedisTemplate redisTemplate;
-
-    /**
-     * key-value: 有过期时间-->token过期时间
-     * 1. tokenMd5:jwtTokenRedisVo
-     * 2. username:loginSysUserRedisVo
-     * 3. username:salt
-     * hash: 没有过期时间，统计在线的用户信息
-     * username:num
-     */
-    @Override
-    public void cacheLoginInfo(JwtToken jwtToken, LoginSysUserVo loginSysUserVo) {
-        if (jwtToken == null) {
-            throw new IllegalArgumentException("jwtToken不能为空");
-        }
-        if (loginSysUserVo == null) {
-            throw new IllegalArgumentException("loginSysUserVo不能为空");
-        }
-        // token
-        String token = jwtToken.getToken();
-        // 盐值
-        String salt = jwtToken.getSalt();
-        // 登录用户名称
-        String username = loginSysUserVo.getUsername();
-        // token md5值
-        String tokenMd5 = DigestUtils.md5Hex(token);
-
-        // Redis缓存JWT Token信息
-        JwtTokenRedisVo jwtTokenRedisVo = ShiroMapstructConvert.INSTANCE.jwtTokenToJwtTokenRedisVo(jwtToken);
-
-        // 用户客户端信息
-        ClientInfo clientInfo = ClientInfoUtil.get(HttpServletRequestUtil.getRequest());
-
-        // Redis缓存登录用户信息
-        // 将LoginSysUserVo对象复制到LoginSysUserRedisVo，使用mapstruct进行对象属性复制
-        LoginSysUserRedisVo loginSysUserRedisVo = LoginSysUserVoConvert.INSTANCE.voToRedisVo(loginSysUserVo);
-        loginSysUserRedisVo.setSalt(salt);
-        loginSysUserRedisVo.setClientInfo(clientInfo);
-
-        // Redis过期时间与JwtToken过期时间一致
-        Duration expireDuration = Duration.ofSeconds(jwtToken.getExpireSecond());
-
-        // 判断是否启用单个用户登录，如果是，这每个用户只有一个有效token
-        boolean singleLogin = jwtProperties.isSingleLogin();
-        if (singleLogin) {
-            deleteUserAllCache(username);
-        }
-
-        // 1. tokenMd5:jwtTokenRedisVo
-        String loginTokenRedisKey = String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5);
-        redisTemplate.opsForValue().set(loginTokenRedisKey, jwtTokenRedisVo, expireDuration);
-        // 2. username:loginSysUserRedisVo
-        redisTemplate.opsForValue().set(String.format(CommonRedisKey.LOGIN_USER, username), loginSysUserRedisVo, expireDuration);
-        // 3. salt hash,方便获取盐值鉴权
-        redisTemplate.opsForValue().set(String.format(CommonRedisKey.LOGIN_SALT, username), salt, expireDuration);
-        // 4. login user token
-        redisTemplate.opsForValue().set(String.format(CommonRedisKey.LOGIN_USER_TOKEN, username, tokenMd5), loginTokenRedisKey, expireDuration);
-    }
-
-    @Override
-    public void refreshLoginInfo(String oldToken, String username, JwtToken newJwtToken) {
-        // 获取缓存的登录用户信息
-        LoginSysUserRedisVo loginSysUserRedisVo = getLoginSysUserRedisVo(username);
-        // 删除之前的token信息
-        deleteLoginInfo(oldToken, username);
-        // 缓存登录信息
-        cacheLoginInfo(newJwtToken, loginSysUserRedisVo);
-    }
-
-    @Override
-    public LoginSysUserRedisVo getLoginSysUserRedisVo(String username) {
-        if (StringUtils.isBlank(username)) {
-            throw new IllegalArgumentException("username不能为空");
-        }
-        return (LoginSysUserRedisVo) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_USER, username));
-    }
-
-    @Override
-    public LoginSysUserVo getLoginSysUserVo(String username) {
-        if (StringUtils.isBlank(username)) {
-            throw new IllegalArgumentException("username不能为空");
-        }
-        LoginSysUserRedisVo userRedisVo = getLoginSysUserRedisVo(username);
-        return userRedisVo;
-    }
-
-    @Override
-    public String getSalt(String username) {
-        if (StringUtils.isBlank(username)) {
-            throw new IllegalArgumentException("username不能为空");
-        }
-        String salt = (String) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_SALT, username));
-        return salt;
-    }
-
-    @Override
-    public void deleteLoginInfo(String token, String username) {
-        if (token == null) {
-            throw new IllegalArgumentException("token不能为空");
-        }
-        if (username == null) {
-            throw new IllegalArgumentException("username不能为空");
-        }
-        String tokenMd5 = DigestUtils.md5Hex(token);
-        // 1. delete tokenMd5
-        redisTemplate.delete(String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5));
-        // 2. delete username
-        redisTemplate.delete(String.format(CommonRedisKey.LOGIN_USER, username));
-        // 3. delete salt
-        redisTemplate.delete(String.format(CommonRedisKey.LOGIN_SALT, username));
-        // 4. delete user token
-        redisTemplate.delete(String.format(CommonRedisKey.LOGIN_USER_TOKEN, username, tokenMd5));
-    }
-
-    @Override
-    public boolean exists(String token) {
-        if (token == null) {
-            throw new IllegalArgumentException("token不能为空");
-        }
-        String tokenMd5 = DigestUtils.md5Hex(token);
-        Object object = redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_TOKEN, tokenMd5));
-        return object != null;
-    }
-
-    @Override
-    public void deleteUserAllCache(String username) {
-        Set<String> userTokenMd5Set = redisTemplate.keys(String.format(CommonRedisKey.LOGIN_USER_ALL_TOKEN, username));
-        if (CollectionUtils.isEmpty(userTokenMd5Set)) {
-            return;
-        }
-
-        // 1. 删除登录用户的所有token信息
-        List<String> tokenMd5List = redisTemplate.opsForValue().multiGet(userTokenMd5Set);
-        redisTemplate.delete(tokenMd5List);
-        // 2. 删除登录用户的所有user:token信息
-        redisTemplate.delete(userTokenMd5Set);
-        // 3. 删除登录用户信息
-        redisTemplate.delete(String.format(CommonRedisKey.LOGIN_USER, username));
-        // 4. 删除登录用户盐值信息
-        redisTemplate.delete(String.format(CommonRedisKey.LOGIN_SALT, username));
-    }
-
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/convert/LoginSysUserVoConvert.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/convert/LoginSysUserVoConvert.java
deleted file mode 100644
index c1dc5e33..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/convert/LoginSysUserVoConvert.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.convert;
-
-import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserRedisVo;
-import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-
-/**
- * 登录系统用户VO对象属性复制转换器
- *
- * @author geekidea
- * @date 2020/3/24
- **/
-@Mapper
-public interface LoginSysUserVoConvert {
-
-    LoginSysUserVoConvert INSTANCE = Mappers.getMapper(LoginSysUserVoConvert.class);
-
-    /**
-     * LoginSysUserVo对象转换成LoginSysUserRedisVo
-     *
-     * @param loginSysUserVo
-     * @return
-     */
-    LoginSysUserRedisVo voToRedisVo(LoginSysUserVo loginSysUserVo);
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/convert/ShiroMapstructConvert.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/convert/ShiroMapstructConvert.java
deleted file mode 100644
index 2b6fc040..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/convert/ShiroMapstructConvert.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.convert;
-
-import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
-import io.geekidea.springbootplus.framework.shiro.vo.JwtTokenRedisVo;
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-
-/**
- * Shiro包下使用mapstruct对象属性复制转换器
- *
- * @author geekidea
- * @date 2019-09-30
- * @since 1.3.0.RELEASE
- **/
-@Mapper
-public interface ShiroMapstructConvert {
-
-    ShiroMapstructConvert INSTANCE = Mappers.getMapper(ShiroMapstructConvert.class);
-
-    /**
-     * JwtToken对象转换成JwtTokenRedisVo
-     *
-     * @param jwtToken
-     * @return
-     */
-    JwtTokenRedisVo jwtTokenToJwtTokenRedisVo(JwtToken jwtToken);
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/exception/ShiroConfigException.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/exception/ShiroConfigException.java
deleted file mode 100644
index 71c3f7a4..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/exception/ShiroConfigException.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.exception;
-
-import io.geekidea.springbootplus.framework.common.api.ApiCode;
-import io.geekidea.springbootplus.framework.common.exception.SpringBootPlusException;
-
-/**
- * Shiro配置异常
- *
- * @author geekidea
- * @date 2019-09-29
- * @since 1.3.0.RELEASE
- **/
-public class ShiroConfigException extends SpringBootPlusException {
-	private static final long serialVersionUID = -4573955712491628431L;
-
-	public ShiroConfigException(String message) {
-        super(message);
-    }
-
-    public ShiroConfigException(Integer errorCode, String message) {
-        super(errorCode, message);
-    }
-
-    public ShiroConfigException(ApiCode apiCode) {
-        super(apiCode);
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/jwt/JwtCredentialsMatcher.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/jwt/JwtCredentialsMatcher.java
deleted file mode 100644
index 2d211f61..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/jwt/JwtCredentialsMatcher.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.jwt;
-
-import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.authc.AuthenticationInfo;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.credential.CredentialsMatcher;
-
-/**
- * JWT证书匹配
- *
- * @author geekidea
- * @date 2019-09-30
- * @since 1.3.0.RELEASE
- **/
-@Slf4j
-public class JwtCredentialsMatcher implements CredentialsMatcher {
-
-    @Override
-    public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
-        String token = authenticationToken.getCredentials().toString();
-        String salt = authenticationInfo.getCredentials().toString();
-        try {
-            return JwtUtil.verifyToken(token, salt);
-        } catch (Exception e) {
-            log.error("JWT Token CredentialsMatch Exception:" + e.getMessage(), e);
-        }
-        return false;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/jwt/JwtFilter.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/jwt/JwtFilter.java
deleted file mode 100644
index bca4d2b8..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/jwt/JwtFilter.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.jwt;
-
-import io.geekidea.springbootplus.config.properties.JwtProperties;
-import io.geekidea.springbootplus.framework.common.api.ApiCode;
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.shiro.cache.LoginRedisService;
-import io.geekidea.springbootplus.framework.shiro.service.ShiroLoginService;
-import io.geekidea.springbootplus.framework.shiro.util.JwtTokenUtil;
-import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
-import io.geekidea.springbootplus.framework.util.HttpServletResponseUtil;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.subject.Subject;
-import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
-import org.apache.shiro.web.util.WebUtils;
-
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Shiro JWT授权过滤器
- *
- * @author geekidea
- * @date 2019-09-27
- * @since 1.3.0.RELEASE
- **/
-@Slf4j
-public class JwtFilter extends AuthenticatingFilter {
-
-    private ShiroLoginService shiroLoginService;
-
-    private LoginRedisService loginRedisService;
-
-    private JwtProperties jwtProperties;
-
-    public JwtFilter(ShiroLoginService shiroLoginService, LoginRedisService loginRedisService, JwtProperties jwtProperties) {
-        this.shiroLoginService = shiroLoginService;
-        this.loginRedisService = loginRedisService;
-        this.jwtProperties = jwtProperties;
-    }
-
-    /**
-     * 将JWT Token包装成AuthenticationToken
-     *
-     * @param servletRequest
-     * @param servletResponse
-     * @return
-     * @throws Exception
-     */
-    @Override
-    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
-        String token = JwtTokenUtil.getToken();
-        if (StringUtils.isBlank(token)) {
-            throw new AuthenticationException("token不能为空");
-        }
-        if (JwtUtil.isExpired(token)) {
-            throw new AuthenticationException("JWT Token已过期,token:" + token);
-        }
-
-        // 如果开启redis二次校验，或者设置为单个用户token登录，则先在redis中判断token是否存在
-        if (jwtProperties.isRedisCheck() || jwtProperties.isSingleLogin()) {
-            boolean redisExpired = loginRedisService.exists(token);
-            if (!redisExpired) {
-                throw new AuthenticationException("Redis Token不存在,token:" + token);
-            }
-        }
-
-        String username = JwtUtil.getUsername(token);
-        String salt;
-        if (jwtProperties.isSaltCheck()){
-            salt = loginRedisService.getSalt(username);
-        }else{
-            salt = jwtProperties.getSecret();
-        }
-        return JwtToken.build(token, username, salt, jwtProperties.getExpireSecond());
-    }
-
-    /**
-     * 访问失败处理
-     *
-     * @param request
-     * @param response
-     * @return
-     * @throws Exception
-     */
-    @Override
-    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
-        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
-        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
-        // 返回401
-        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
-        // 设置响应码为401或者直接输出消息
-        String url = httpServletRequest.getRequestURI();
-        log.error("onAccessDenied url：{}", url);
-        ApiResult<Boolean> apiResult = ApiResult.fail(ApiCode.UNAUTHORIZED);
-        HttpServletResponseUtil.printJson(httpServletResponse, apiResult);
-        return false;
-    }
-
-    /**
-     * 判断是否允许访问
-     *
-     * @param request
-     * @param response
-     * @param mappedValue
-     * @return
-     */
-    @Override
-    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
-        String url = WebUtils.toHttp(request).getRequestURI();
-        log.debug("isAccessAllowed url:{}", url);
-        if (this.isLoginRequest(request, response)) {
-            return true;
-        }
-        boolean allowed = false;
-        try {
-            allowed = executeLogin(request, response);
-        } catch (IllegalStateException e) { //not found any token
-            log.error("Token不能为空", e);
-        } catch (Exception e) {
-            log.error("访问错误", e);
-        }
-        return allowed || super.isPermissive(mappedValue);
-    }
-
-    /**
-     * 登录成功处理
-     *
-     * @param token
-     * @param subject
-     * @param request
-     * @param response
-     * @return
-     * @throws Exception
-     */
-    @Override
-    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
-        String url = WebUtils.toHttp(request).getRequestURI();
-        log.debug("鉴权成功,token:{},url:{}", token, url);
-        // 刷新token
-        JwtToken jwtToken = (JwtToken) token;
-        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
-        shiroLoginService.refreshToken(jwtToken, httpServletResponse);
-        return true;
-    }
-
-    /**
-     * 登录失败处理
-     *
-     * @param token
-     * @param e
-     * @param request
-     * @param response
-     * @return
-     */
-    @Override
-    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
-        log.error("登录失败，token:" + token + ",error:" + e.getMessage(), e);
-        return false;
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/jwt/JwtRealm.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/jwt/JwtRealm.java
deleted file mode 100644
index e9244913..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/jwt/JwtRealm.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.jwt;
-
-import io.geekidea.springbootplus.framework.shiro.cache.LoginRedisService;
-import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserRedisVo;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.collections4.SetUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.authc.AuthenticationInfo;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.SimpleAuthenticationInfo;
-import org.apache.shiro.authz.AuthorizationInfo;
-import org.apache.shiro.authz.SimpleAuthorizationInfo;
-import org.apache.shiro.realm.AuthorizingRealm;
-import org.apache.shiro.subject.PrincipalCollection;
-
-/**
- * Shiro 授权认证
- *
- * @author geekidea
- * @date 2019-09-27
- * @since 1.3.0.RELEASE
- **/
-@Slf4j
-public class JwtRealm extends AuthorizingRealm {
-
-    private LoginRedisService loginRedisService;
-
-    public JwtRealm(LoginRedisService loginRedisService) {
-        this.loginRedisService = loginRedisService;
-    }
-
-    @Override
-    public boolean supports(AuthenticationToken token) {
-        return token != null && token instanceof JwtToken;
-    }
-
-    /**
-     * 授权认证,设置角色/权限信息
-     *
-     * @param principalCollection
-     * @return
-     */
-    @Override
-    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
-        log.debug("doGetAuthorizationInfo principalCollection...");
-        // 设置角色/权限信息
-        JwtToken jwtToken = (JwtToken) principalCollection.getPrimaryPrincipal();
-        // 获取username
-        String username = jwtToken.getUsername();
-        // 获取登录用户角色权限信息
-        LoginSysUserRedisVo loginSysUserRedisVo = loginRedisService.getLoginSysUserRedisVo(username);
-        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
-        // 设置角色
-        authorizationInfo.setRoles(SetUtils.hashSet(loginSysUserRedisVo.getRoleCode()));
-        // 设置权限
-        authorizationInfo.setStringPermissions(loginSysUserRedisVo.getPermissionCodes());
-        return authorizationInfo;
-    }
-
-    /**
-     * 登录认证
-     *
-     * @param authenticationToken
-     * @return
-     * @throws AuthenticationException
-     */
-    @Override
-    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
-        log.debug("doGetAuthenticationInfo authenticationToken...");
-        // 校验token
-        JwtToken jwtToken = (JwtToken) authenticationToken;
-        if (jwtToken == null) {
-            throw new AuthenticationException("jwtToken不能为空");
-        }
-        String salt = jwtToken.getSalt();
-        if (StringUtils.isBlank(salt)) {
-            throw new AuthenticationException("salt不能为空");
-        }
-        return new SimpleAuthenticationInfo(
-                jwtToken,
-                salt,
-                getName()
-        );
-
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/jwt/JwtToken.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/jwt/JwtToken.java
deleted file mode 100644
index 1eb668f0..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/jwt/JwtToken.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.jwt;
-
-import com.auth0.jwt.interfaces.DecodedJWT;
-import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
-import io.geekidea.springbootplus.framework.util.IpUtil;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.apache.shiro.authc.HostAuthenticationToken;
-
-import java.util.Date;
-
-/**
- * Shiro JwtToken对象
- *
- * @author geekidea
- * @date 2019-09-27
- * @since 1.3.0.RELEASE
- **/
-@Data
-@Accessors(chain = true)
-public class JwtToken implements HostAuthenticationToken {
-	private static final long serialVersionUID = 5101247566043093405L;
-	
-	/**
-     * 登录ip
-     */
-    private String host;
-    /**
-     * 登录用户名称
-     */
-    private String username;
-    /**
-     * 登录盐值
-     */
-    private String salt;
-    /**
-     * 登录token
-     */
-    private String token;
-    /**
-     * 创建时间
-     */
-    private Date createDate;
-    /**
-     * 多长时间过期，默认一小时
-     */
-    private long expireSecond;
-    /**
-     * 过期日期
-     */
-    private Date expireDate;
-
-    private String principal;
-
-    private String credentials;
-
-    @Override
-    public Object getPrincipal() {
-        return token;
-    }
-
-    @Override
-    public Object getCredentials() {
-        return token;
-    }
-
-    public static JwtToken build(String token, String username, String salt, long expireSecond) {
-        DecodedJWT decodedJwt = JwtUtil.getJwtInfo(token);
-        Date createDate = decodedJwt.getIssuedAt();
-        Date expireDate = decodedJwt.getExpiresAt();
-        return new JwtToken()
-                .setUsername(username)
-                .setToken(token)
-                .setHost(IpUtil.getRequestIp())
-                .setSalt(salt)
-                .setCreateDate(createDate)
-                .setExpireSecond(expireSecond)
-                .setExpireDate(expireDate);
-
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/service/LoginToken.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/service/LoginToken.java
deleted file mode 100644
index b61bd251..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/service/LoginToken.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.service;
-
-import java.io.Serializable;
-
-/**
- * 获取登录token
- *
- * @author geekidea
- * @date 2020/3/25
- **/
-public interface LoginToken extends Serializable {
-
-    /**
-     * 获取登录token
-     *
-     * @return
-     */
-    String getToken();
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/service/LoginUsername.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/service/LoginUsername.java
deleted file mode 100644
index 52abca9d..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/service/LoginUsername.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.service;
-
-import java.io.Serializable;
-
-/**
- * 获取登录用户名称
- *
- * @author geekidea
- * @date 2020/3/24
- **/
-public interface LoginUsername extends Serializable {
-
-    /**
-     * 获取用户名
-     *
-     * @return
-     */
-    String getUsername();
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/service/ShiroLoginService.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/service/ShiroLoginService.java
deleted file mode 100644
index 577895dc..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/service/ShiroLoginService.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.service;
-
-import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
-
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * @author geekidea
- * @date 2020/3/24
- **/
-public interface ShiroLoginService {
-    /**
-     * 如果(当前时间+倒计时) > 过期时间，则刷新token
-     * 并更新缓存
-     * 当前token失效，返回新token
-     * 当前请求有效，返回状态码：460
-     * 前端下次使用新token
-     * 如果token继续发往后台，则提示，此token已失效，请使用新token，不在返回新token，返回状态码：461
-     *
-     * @param jwtToken
-     * @param httpServletResponse
-     * @throws Exception
-     */
-    void refreshToken(JwtToken jwtToken, HttpServletResponse httpServletResponse) throws Exception;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/service/impl/ShiroLoginServiceImpl.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/service/impl/ShiroLoginServiceImpl.java
deleted file mode 100644
index cfe5cc2c..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/service/impl/ShiroLoginServiceImpl.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.service.impl;
-
-import io.geekidea.springbootplus.config.constant.CommonConstant;
-import io.geekidea.springbootplus.config.properties.JwtProperties;
-import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties;
-import io.geekidea.springbootplus.framework.shiro.cache.LoginRedisService;
-import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
-import io.geekidea.springbootplus.framework.shiro.service.ShiroLoginService;
-import io.geekidea.springbootplus.framework.shiro.util.JwtTokenUtil;
-import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.time.DateUtils;
-import org.apache.shiro.authc.AuthenticationException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.stereotype.Service;
-
-import javax.servlet.http.HttpServletResponse;
-import java.time.Duration;
-import java.util.Date;
-
-/**
- * Shiro登录服务
- *
- * @author geekidea
- * @date 2020/3/24
- **/
-@Slf4j
-@Service
-public class ShiroLoginServiceImpl implements ShiroLoginService {
-
-
-    @Lazy
-    @Autowired
-    private LoginRedisService loginRedisService;
-
-    @Lazy
-    @Autowired
-    private JwtProperties jwtProperties;
-
-    @Lazy
-    @Autowired
-    private SpringBootPlusProperties springBootPlusProperties;
-
-    @Lazy
-    @Autowired
-    private RedisTemplate redisTemplate;
-
-
-    @Override
-    public void refreshToken(JwtToken jwtToken, HttpServletResponse httpServletResponse) throws Exception {
-        if (jwtToken == null) {
-            return;
-        }
-        String token = jwtToken.getToken();
-        if (StringUtils.isBlank(token)) {
-            return;
-        }
-        // 判断是否刷新token
-        boolean isRefreshToken = jwtProperties.isRefreshToken();
-        if (!isRefreshToken) {
-            return;
-        }
-        // 获取过期时间
-        Date expireDate = JwtUtil.getExpireDate(token);
-        // 获取倒计时
-        Integer countdown = jwtProperties.getRefreshTokenCountdown();
-        // 如果(当前时间+倒计时) > 过期时间，则刷新token
-        boolean refresh = DateUtils.addSeconds(new Date(), countdown).after(expireDate);
-
-        if (!refresh) {
-            return;
-        }
-
-        // 如果token继续发往后台，则提示，此token已失效，请使用新token，不在返回新token，返回状态码：461
-        // 如果Redis缓存中没有，JwtToken没有过期，则说明，已经刷新token
-        boolean exists = loginRedisService.exists(token);
-        if (!exists) {
-            httpServletResponse.setStatus(CommonConstant.JWT_INVALID_TOKEN_CODE);
-            throw new AuthenticationException("token已无效，请使用已刷新的token");
-        }
-        String username = jwtToken.getUsername();
-        String salt = jwtToken.getSalt();
-        Long expireSecond = jwtProperties.getExpireSecond();
-        // 生成新token字符串
-        String newToken = JwtUtil.generateToken(username, salt, Duration.ofSeconds(expireSecond));
-        // 生成新JwtToken对象
-        JwtToken newJwtToken = JwtToken.build(newToken, username, salt, expireSecond);
-        // 更新redis缓存
-        loginRedisService.refreshLoginInfo(token, username, newJwtToken);
-        log.debug("刷新token成功，原token:{}，新token:{}", token, newToken);
-        // 设置响应头
-        // 刷新token
-        httpServletResponse.setStatus(CommonConstant.JWT_REFRESH_TOKEN_CODE);
-        httpServletResponse.setHeader(JwtTokenUtil.getTokenName(), newToken);
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/util/JwtTokenUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/util/JwtTokenUtil.java
deleted file mode 100644
index 74ecc355..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/util/JwtTokenUtil.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.util;
-
-import io.geekidea.springbootplus.config.properties.JwtProperties;
-import io.geekidea.springbootplus.framework.util.HttpServletRequestUtil;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.stereotype.Component;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * JwtToken工具类
- *
- * @author geekidea
- * @date 2019-10-03
- * @since 1.3.0.RELEASE
- **/
-@Slf4j
-@Component
-public class JwtTokenUtil {
-
-    private static String tokenName;
-
-    public JwtTokenUtil(JwtProperties jwtProperties) {
-        tokenName = jwtProperties.getTokenName();
-        log.debug("tokenName:{}", tokenName);
-    }
-
-    /**
-     * 获取token名称
-     *
-     * @return
-     */
-    public static String getTokenName() {
-        return tokenName;
-    }
-
-    /**
-     * 从请求头或者请求参数中
-     *
-     * @return
-     */
-    public static String getToken() {
-        return getToken(HttpServletRequestUtil.getRequest());
-    }
-
-    /**
-     * 从请求头或者请求参数中
-     *
-     * @param request
-     * @return
-     */
-    public static String getToken(HttpServletRequest request) {
-        if (request == null) {
-            throw new IllegalArgumentException("request不能为空");
-        }
-        // 从请求头中获取token
-        String token = request.getHeader(tokenName);
-        if (StringUtils.isBlank(token)) {
-            // 从请求参数中获取token
-            token = request.getParameter(tokenName);
-        }
-        return token;
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/util/JwtUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/util/JwtUtil.java
deleted file mode 100644
index fe1c53fc..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/util/JwtUtil.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.util;
-
-import com.alibaba.fastjson.JSON;
-import com.auth0.jwt.JWT;
-import com.auth0.jwt.JWTVerifier;
-import com.auth0.jwt.algorithms.Algorithm;
-import com.auth0.jwt.interfaces.DecodedJWT;
-import io.geekidea.springbootplus.config.constant.CommonConstant;
-import io.geekidea.springbootplus.config.properties.JwtProperties;
-import io.geekidea.springbootplus.framework.util.UUIDUtil;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.time.DateUtils;
-import org.springframework.stereotype.Component;
-
-import java.time.Duration;
-import java.util.Date;
-
-/**
- * JWT工具类
- * https://github.com/auth0/java-jwt
- *
- * @author geekidea
- * @date 2019-09-30
- * @since 1.3.0.RELEASE
- **/
-@Slf4j
-@Component
-public class JwtUtil {
-
-    private static JwtProperties jwtProperties;
-
-    public JwtUtil(JwtProperties jwtProperties) {
-        JwtUtil.jwtProperties = jwtProperties;
-        log.info(JSON.toJSONString(JwtUtil.jwtProperties));
-    }
-
-    /**
-     * 生成JWT Token
-     *
-     * @param username       用户名
-     * @param salt           盐值
-     * @param expireDuration 过期时间和单位
-     * @return token
-     */
-    public static String generateToken(String username, String salt, Duration expireDuration) {
-        try {
-            if (StringUtils.isBlank(username)) {
-                log.error("username不能为空");
-                return null;
-            }
-            log.debug("username:{}", username);
-
-            // 如果盐值为空，则使用默认值：666666
-            if (StringUtils.isBlank(salt)) {
-                salt = jwtProperties.getSecret();
-            }
-            log.debug("salt:{}", salt);
-
-            // 过期时间，单位：秒
-            Long expireSecond;
-            // 默认过期时间为1小时
-            if (expireDuration == null) {
-                expireSecond = jwtProperties.getExpireSecond();
-            } else {
-                expireSecond = expireDuration.getSeconds();
-            }
-            log.debug("expireSecond:{}", expireSecond);
-            Date expireDate = DateUtils.addSeconds(new Date(), expireSecond.intValue());
-            log.debug("expireDate:{}", expireDate);
-
-            // 生成token
-            Algorithm algorithm = Algorithm.HMAC256(salt);
-            String token = JWT.create()
-                    .withClaim(CommonConstant.JWT_USERNAME, username)
-                    // jwt唯一id
-                    .withJWTId(UUIDUtil.getUuid())
-                    // 签发人
-                    .withIssuer(jwtProperties.getIssuer())
-                    // 主题
-                    .withSubject(jwtProperties.getSubject())
-                    // 签发的目标
-                    .withAudience(jwtProperties.getAudience())
-                    // 签名时间
-                    .withIssuedAt(new Date())
-                    // token过期时间
-                    .withExpiresAt(expireDate)
-                    // 签名
-                    .sign(algorithm);
-            return token;
-        } catch (Exception e) {
-            log.error("generateToken exception", e);
-        }
-        return null;
-    }
-
-    public static boolean verifyToken(String token, String salt) {
-        try {
-            Algorithm algorithm = Algorithm.HMAC256(salt);
-            JWTVerifier verifier = JWT.require(algorithm)
-                    // 签发人
-                    .withIssuer(jwtProperties.getIssuer())
-                    // 主题
-                    .withSubject(jwtProperties.getSubject())
-                    // 签发的目标
-                    .withAudience(jwtProperties.getAudience())
-                    .build();
-            DecodedJWT jwt = verifier.verify(token);
-            if (jwt != null) {
-                return true;
-            }
-        } catch (Exception e) {
-            log.error("Verify Token Exception", e);
-        }
-        return false;
-    }
-
-    /**
-     * 解析token，获取token数据
-     *
-     * @param token
-     * @return
-     */
-    public static DecodedJWT getJwtInfo(String token) {
-        return JWT.decode(token);
-    }
-
-    /**
-     * 获取用户名
-     *
-     * @param token
-     * @return
-     */
-    public static String getUsername(String token) {
-        if (StringUtils.isBlank(token)){
-            return null;
-        }
-        DecodedJWT decodedJwt = getJwtInfo(token);
-        if (decodedJwt == null) {
-            return null;
-        }
-        String username = decodedJwt.getClaim(CommonConstant.JWT_USERNAME).asString();
-        return username;
-    }
-
-    /**
-     * 获取创建时间
-     *
-     * @param token
-     * @return
-     */
-    public static Date getIssuedAt(String token) {
-        DecodedJWT decodedJwt = getJwtInfo(token);
-        if (decodedJwt == null) {
-            return null;
-        }
-        return decodedJwt.getIssuedAt();
-    }
-
-    /**
-     * 获取过期时间
-     *
-     * @param token
-     * @return
-     */
-    public static Date getExpireDate(String token) {
-        DecodedJWT decodedJwt = getJwtInfo(token);
-        if (decodedJwt == null) {
-            return null;
-        }
-        return decodedJwt.getExpiresAt();
-    }
-
-    /**
-     * 判断token是否已过期
-     *
-     * @param token
-     * @return
-     */
-    public static boolean isExpired(String token) {
-        Date expireDate = getExpireDate(token);
-        if (expireDate == null) {
-            return true;
-        }
-        return expireDate.before(new Date());
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/util/SaltUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/util/SaltUtil.java
deleted file mode 100644
index 0424cfbc..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/util/SaltUtil.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.util;
-
-import io.geekidea.springbootplus.config.properties.JwtProperties;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.shiro.crypto.SecureRandomNumberGenerator;
-
-/**
- * 盐值包装工具类
- *
- * @author geekidea
- * @date 2019-09-30
- * @since 1.3.0.RELEASE
- **/
-public class SaltUtil {
-
-    /**
-     * 盐值包装
-     *
-     * @param secret 配置文件中配置的附加盐值
-     * @param salt   数据库中保存的盐值
-     * @return
-     */
-    public static String getSalt(String secret, String salt) {
-        if (StringUtils.isBlank(secret) && StringUtils.isBlank(salt)) {
-            return null;
-        }
-        // 加密方法
-        String newSalt = DigestUtils.sha256Hex(secret + salt);
-        return newSalt;
-    }
-
-    /**
-     * 生成32位随机盐
-     *
-     * @return
-     */
-    public static String generateSalt() {
-        return new SecureRandomNumberGenerator().nextBytes(16).toHex();
-    }
-
-    /**
-     * 加工盐值
-     *
-     * @param salt
-     * @param jwtProperties
-     * @return
-     */
-    public static String getSalt(String salt, JwtProperties jwtProperties) {
-        String newSalt;
-        if (jwtProperties.isSaltCheck()) {
-            // 包装盐值
-            newSalt = SaltUtil.getSalt(jwtProperties.getSecret(), salt);
-        } else {
-            newSalt = jwtProperties.getSecret();
-        }
-        return newSalt;
-    }
-
-}
-
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/vo/JwtTokenRedisVo.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/vo/JwtTokenRedisVo.java
deleted file mode 100644
index 085fa41b..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/vo/JwtTokenRedisVo.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.vo;
-
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Date;
-
-/**
- * JwtToken Redis缓存对象
- *
- * @author geekidea
- * @date 2019-09-30
- **/
-@Data
-@Accessors(chain = true)
-public class JwtTokenRedisVo implements Serializable {
-    private static final long serialVersionUID = 1831633309466775223L;
-    /**
-     * 登录ip
-     */
-    private String host;
-    /**
-     * 登录用户名称
-     */
-    private String username;
-    /**
-     * 登录盐值
-     */
-    private String salt;
-    /**
-     * 登录token
-     */
-    private String token;
-    /**
-     * 创建时间
-     */
-    private Date createDate;
-    /**
-     * 多长时间过期，默认一小时
-     */
-    private long expireSecond;
-    /**
-     * 过期日期
-     */
-    private Date expireDate;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/vo/LoginSysUserRedisVo.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/vo/LoginSysUserRedisVo.java
deleted file mode 100644
index ade7efe1..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/vo/LoginSysUserRedisVo.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.vo;
-
-import io.geekidea.springbootplus.framework.common.bean.ClientInfo;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-/**
- * 登录用户Redis对象，后台使用
- *
- * @author geekidea
- * @date 2019-9-30
- **/
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = false)
-public class LoginSysUserRedisVo extends LoginSysUserVo {
-
-    private static final long serialVersionUID = -3858850188055605806L;
-
-    /**
-     * 包装后的盐值
-     */
-    private String salt;
-
-    /**
-     * 登录ip
-     */
-    private ClientInfo clientInfo;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/vo/LoginSysUserVo.java b/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/vo/LoginSysUserVo.java
deleted file mode 100644
index fa27c147..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/shiro/vo/LoginSysUserVo.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.shiro.vo;
-
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Set;
-
-/**
- * <p>
- * 登录用户对象，响应给前端
- * </p>
- *
- * @author geekidea
- * @date 2019-05-15
- **/
-@Data
-@Accessors(chain = true)
-public class LoginSysUserVo implements Serializable {
-
-    private static final long serialVersionUID = -1758338570596088158L;
-
-    @ApiModelProperty("主键")
-    private Long id;
-
-    @ApiModelProperty("用户名")
-    private String username;
-
-    @ApiModelProperty("昵称")
-    private String nickname;
-
-    @ApiModelProperty("性别，0：女，1：男，默认1")
-    private Integer gender;
-
-    @ApiModelProperty("状态，0：禁用，1：启用，2：锁定")
-    private Integer state;
-
-    @ApiModelProperty("部门id")
-    private Long departmentId;
-
-    @ApiModelProperty("部门名称")
-    private String departmentName;
-
-    @ApiModelProperty("角色id")
-    private Long roleId;
-
-    @ApiModelProperty("角色名称")
-    private String roleName;
-
-    @ApiModelProperty("角色编码")
-    private String roleCode;
-
-    @ApiModelProperty("权限编码列表")
-    private Set<String> permissionCodes;
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/AnsiUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/AnsiUtil.java
deleted file mode 100644
index 9e4c5b15..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/AnsiUtil.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import lombok.extern.slf4j.Slf4j;
-import org.fusesource.jansi.Ansi;
-import org.springframework.core.env.Environment;
-
-/**
- * @author geekidea
- * @date 2018-11-08
- */
-@Slf4j
-public class AnsiUtil {
-
-    private static boolean enableAnsi;
-
-    static {
-        Boolean value = false;
-        try {
-            Environment environment = SpringContextUtil.getBean(Environment.class);
-            value = environment.getProperty("spring-boot-plus.enable-ansi",boolean.class);
-            value = value == null ? false : value;
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        enableAnsi = value;
-    }
-
-    public static String getAnsi(Ansi.Color color,String text){
-
-        if (enableAnsi){
-            return Ansi.ansi().eraseScreen().fg(color).a(text).reset().toString();
-        }
-        return text;
-    }
-
-    public static String getAnsi(Ansi.Color color,String text,boolean flag){
-        if (flag){
-            return Ansi.ansi().eraseScreen().fg(color).a(text).reset().toString();
-        }
-        return text;
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/BaseEnumUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/BaseEnumUtil.java
deleted file mode 100644
index 5fd63fdd..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/BaseEnumUtil.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import io.geekidea.springbootplus.framework.common.enums.BaseEnum;
-import io.geekidea.springbootplus.framework.common.vo.EnumVo;
-import org.apache.commons.collections4.MapUtils;
-
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * BaseEnum枚举工具类
- *
- * @author geekidea
- * @date 2018-11-08
- */
-public class BaseEnumUtil {
-
-    private static final Map<String, Map<Integer, EnumVo<? extends BaseEnum>>> ENUM_MAP = new LinkedHashMap<>();
-
-    /**
-     * 通过code获取描述
-     *
-     * @param baseEnumType
-     * @param code
-     * @return
-     */
-    public static BaseEnum getEnum(Class<? extends BaseEnum> baseEnumType, Integer code) {
-        EnumVo<? extends BaseEnum> enumVo = getEnumVo(baseEnumType, code);
-        if (enumVo == null) {
-            return null;
-        }
-        return enumVo.getBaseEnum();
-    }
-
-    /**
-     * 通过code获取描述
-     *
-     * @param baseEnumType
-     * @param code
-     * @return
-     */
-    public static EnumVo<? extends BaseEnum> getEnumVo(Class<? extends BaseEnum> baseEnumType, Integer code) {
-        Map<Integer, EnumVo<? extends BaseEnum>> map = getMap(baseEnumType);
-        if (MapUtils.isEmpty(map)) {
-            return null;
-        }
-        return map.get(code);
-    }
-
-    /**
-     * 判断code在枚举中是否存在
-     *
-     * @param baseEnumType
-     * @param code
-     * @return
-     */
-    public static boolean exists(Class<? extends BaseEnum> baseEnumType, Integer code) {
-        EnumVo<? extends BaseEnum> enumVo = getEnumVo(baseEnumType, code);
-        if (enumVo == null) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * 判断code在枚举中是否不存在
-     *
-     * @param baseEnumType
-     * @param code
-     * @return
-     */
-    public static boolean notExists(Class<? extends BaseEnum> baseEnumType, Integer code) {
-        return !exists(baseEnumType, code);
-    }
-
-    /**
-     * 通过code获取描述
-     *
-     * @param baseEnumType
-     * @param code
-     * @return
-     */
-    public static String getDesc(Class<? extends BaseEnum> baseEnumType, Integer code) {
-        EnumVo<? extends BaseEnum> enumVo = getEnumVo(baseEnumType, code);
-        if (enumVo == null) {
-            return null;
-        }
-        return enumVo.getDesc();
-    }
-
-    /**
-     * 通过类型获取枚举Map
-     *
-     * @param baseEnumType
-     * @return
-     */
-    public static Map<Integer, EnumVo<? extends BaseEnum>> getMap(Class<? extends BaseEnum> baseEnumType) {
-        return ENUM_MAP.get(baseEnumType.getName());
-    }
-
-    /**
-     * 通过类型获取枚举code集合
-     *
-     * @param baseEnumType
-     * @return
-     */
-    public static Set<Integer> getCodeSet(Class<? extends BaseEnum> baseEnumType) {
-        Map<Integer, EnumVo<? extends BaseEnum>> map = getMap(baseEnumType);
-        if (MapUtils.isEmpty(map)) {
-            return null;
-        }
-        return map.keySet();
-    }
-
-    /**
-     * 通过类型获取枚举desc集合
-     *
-     * @param baseEnumType
-     * @return
-     */
-    public static Collection<EnumVo<? extends BaseEnum>> getDescList(Class<? extends BaseEnum> baseEnumType) {
-        Map<Integer, EnumVo<? extends BaseEnum>> map = getMap(baseEnumType);
-        if (MapUtils.isEmpty(map)) {
-            return null;
-        }
-        return map.values();
-    }
-
-    public static Map<String, Map<Integer, EnumVo<? extends BaseEnum>>> getEnumMap() {
-        return ENUM_MAP;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/BrowserUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/BrowserUtil.java
deleted file mode 100644
index b69f3e09..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/BrowserUtil.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * <code>
- * 浏览器工具类<br/>
- * 1.获取当前浏览器名称
- * 2.判断当前用户的浏览器
- * </code>
- *
- * @author geekidea
- * @since 2018-11-08
- */
-public final class BrowserUtil {
-    public static final String IE = "msie";
-    public static final String FIREFOX = "firefox";
-    public static final String CHROME = "chrome";
-
-    private BrowserUtil() {
-        throw new AssertionError();
-    }
-
-    /**
-     * 获取当前浏览器名称
-     *
-     * @param request
-     * @return 返回浏览器名称
-     */
-    public static String getCurrent(HttpServletRequest request) {
-        String userAgent = request.getHeader("USER-AGENT").toLowerCase();
-        if (userAgent != null && !("".equals(userAgent.trim()))) {
-            if (userAgent.indexOf(CHROME) >= 0) {
-                return CHROME;
-            } else if (userAgent.indexOf(FIREFOX) >= 0) {
-                return FIREFOX;
-            } else if (userAgent.indexOf(IE) >= 0) {
-                return IE;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * 是否是IE浏览器
-     *
-     * @param request
-     * @return
-     */
-    public static boolean isIe(HttpServletRequest request) {
-        return IE.equals(getCurrent(request));
-    }
-
-    /**
-     * 是否是Firefox浏览器
-     *
-     * @param request
-     * @return
-     */
-    public static boolean isFirefox(HttpServletRequest request) {
-        return FIREFOX.equals(getCurrent(request));
-    }
-
-    /**
-     * 是否是Chrome浏览器
-     *
-     * @param request
-     * @return
-     */
-    public static boolean isChrome(HttpServletRequest request) {
-        return CHROME.equals(getCurrent(request));
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/ClientInfoUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/ClientInfoUtil.java
deleted file mode 100644
index d3229f52..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/ClientInfoUtil.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import cn.hutool.http.useragent.UserAgent;
-import cn.hutool.http.useragent.UserAgentUtil;
-import io.geekidea.springbootplus.config.constant.CommonConstant;
-import io.geekidea.springbootplus.framework.common.bean.ClientInfo;
-import io.geekidea.springbootplus.framework.common.bean.DeviceInfo;
-
-import javax.servlet.http.HttpServletRequest;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * <p>
- *  用户客户端信息工具类
- * </p>
- * @author geekidea
- * @date 2019-05-24
- **/
-public class ClientInfoUtil {
-
-    private static final Pattern DEVICE_INFO_PATTERN = Pattern.compile(";\\s?(\\S*?\\s?\\S*?)\\s?Build/(\\S*?)[;)]");
-    private static final Pattern DEVICE_INFO_PATTERN_1 = Pattern.compile(";\\s?(\\S*?\\s?\\S*?)\\s?\\)");
-
-    /**
-     * 获取用户客户端信息
-     * @param request
-     * @return
-     */
-    public static ClientInfo get(HttpServletRequest request){
-        String userAgent = request.getHeader(CommonConstant.USER_AGENT);
-        return get(userAgent);
-    }
-
-    /**
-     * 获取用户客户端信息
-     * @param userAgentString
-     * @return
-     */
-    public static ClientInfo get(String userAgentString){
-        ClientInfo clientInfo = new ClientInfo();
-
-        UserAgent userAgent = UserAgentUtil.parse(userAgentString);
-
-        // 浏览器名称
-        clientInfo.setBrowserName(userAgent.getBrowser().getName());
-        // 浏览器版本
-        clientInfo.setBrowserversion(userAgent.getVersion());
-        // 浏览器引擎名称
-        clientInfo.setEngineName(userAgent.getEngine().getName());
-        // 浏览器引擎版本
-        clientInfo.setEngineVersion(userAgent.getEngineVersion());
-        // 用户操作系统名称
-        clientInfo.setOsName(userAgent.getOs().getName());
-        // 用户操作平台名称
-        clientInfo.setPlatformName(userAgent.getPlatform().getName());
-        // 是否是手机
-        clientInfo.setMobile(userAgent.isMobile());
-
-        // 获取移动设备名称和机型
-        DeviceInfo deviceInfo = getDeviceInfo(userAgentString);
-        // 设置移动设备名称和机型
-        clientInfo.setDeviceName(deviceInfo.getName());
-        clientInfo.setDeviceModel(deviceInfo.getModel());
-
-        // ip
-        clientInfo.setIp(IpUtil.getRequestIp());
-
-        return clientInfo;
-    }
-
-    /**
-     * 获取移动端用户设备的名称和机型
-     * @param userAgentString
-     * @return
-     */
-    public static DeviceInfo getDeviceInfo(String userAgentString){
-        DeviceInfo deviceInfo = new DeviceInfo();
-        try {
-
-            Matcher matcher = DEVICE_INFO_PATTERN.matcher(userAgentString);
-            String model = null;
-            String name = null;
-
-            if (matcher.find()) {
-                model = matcher.group(1);
-                name = matcher.group(2);
-            }
-
-            if (model == null && name == null){
-                matcher = DEVICE_INFO_PATTERN_1.matcher(userAgentString);
-                if (matcher.find()) {
-                    model = matcher.group(1);
-                }
-            }
-           
-            deviceInfo.setName(name);
-            deviceInfo.setModel(model);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return deviceInfo;
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/ContentTypeUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/ContentTypeUtil.java
deleted file mode 100644
index 6d95c519..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/ContentTypeUtil.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.support.PropertiesLoaderUtils;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Properties;
-
-/**
- * 获取文件的内容类型
- * mime-type参考：https://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?revision=1752884&view=co
- * @author geekidea
- * @date 2019/08/20
- * @since
- */
-@Slf4j
-public final class ContentTypeUtil {
-
-    private static String MIME_TYPE_CONFIG_FILE = "config/mime-type.properties";
-
-    private static String DEFAULT_MIME_TYPE = "application/octet-stream";
-
-    private static Properties properties;
-
-    static {
-        try {
-            properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource(MIME_TYPE_CONFIG_FILE));
-        } catch (IOException e) {
-            log.error("读取配置文件" + MIME_TYPE_CONFIG_FILE + "异常",e);
-        }
-        log.info(MIME_TYPE_CONFIG_FILE + " = " + properties);
-    }
-
-    /**
-     * 获取文件内容类型
-     * @param file
-     * @return
-     */
-    public static String getContentType(File file){
-        if (file == null){
-            return null;
-        }
-        Path path = Paths.get(file.toURI());
-        if (path == null){
-            return null;
-        }
-        String contentType = null;
-        try {
-            contentType = Files.probeContentType(path);
-        } catch (IOException e) {
-            log.error("获取文件ContentType异常",e);
-        }
-        if (contentType == null){
-            // 读取拓展的自定义配置
-            contentType = getContentTypeByExtension(file);
-        }
-        // 设置默认的内容类型
-        if (contentType == null){
-            contentType = DEFAULT_MIME_TYPE;
-        }
-        return contentType;
-    }
-
-    /**
-     * 根据文件后缀获取自定义配置的文件mime-type
-     * @param file
-     * @return
-     */
-    private static String getContentTypeByExtension(File file){
-        if (properties == null){
-            return null;
-        }
-        String extension = FilenameUtils.getExtension(file.getName());
-        if (StringUtils.isBlank(extension)){
-            return null;
-        }
-        String contentType = properties.getProperty(extension);
-        return contentType;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/DateUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/DateUtil.java
deleted file mode 100644
index 20c70a7d..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/DateUtil.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import io.geekidea.springbootplus.config.constant.DatePattern;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-/**
- * @author geekidea
- * @date 2018-11-08
- */
-public class DateUtil {
-
-    public static String getDateString(Date date){
-        if (date == null){
-            return null;
-        }
-        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DatePattern.YYYY_MM_DD);
-        String dateString = simpleDateFormat.format(date);
-        return dateString;
-    }
-
-    public static String getDateTimeString(Date date){
-        if (date == null){
-            return null;
-        }
-        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DatePattern.YYYY_MM_DD_HH_MM_SS);
-        String dateString = simpleDateFormat.format(date);
-        return dateString;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/DownloadUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/DownloadUtil.java
deleted file mode 100644
index 58415ce2..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/DownloadUtil.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import io.geekidea.springbootplus.config.constant.CommonConstant;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.util.Base64Utils;
-import org.springframework.util.CollectionUtils;
-import org.springframework.util.FileCopyUtils;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.*;
-import java.net.URLEncoder;
-import java.util.List;
-
-/**
- * 文件下载工具类
- *
- * @author geekidea
- * @date 2019/8/21
- * @since 1.2.1-RELEASE
- */
-@Slf4j
-public final class DownloadUtil {
-
-    /**
-     * 下载文件，使用默认下载处理器
-     *
-     * @param downloadDir
-     * @param downloadFileName
-     * @param allowFileExtensions
-     * @param response
-     * @throws Exception
-     */
-    public static void download(String downloadDir, String downloadFileName, List<String> allowFileExtensions, HttpServletResponse response) throws Exception {
-        download(downloadDir, downloadFileName, allowFileExtensions, response, new DefaultDownloadHandler());
-    }
-
-    /**
-     * 下载文件，使用自定义下载处理器
-     *
-     * @param downloadDir      文件目录
-     * @param downloadFileName 文件名称
-     * @throws Exception
-     */
-    public static void download(String downloadDir, String downloadFileName, List<String> allowFileExtensions, HttpServletResponse response, DownloadHandler downloadHandler) throws Exception {
-        log.info("downloadDir:{}", downloadDir);
-        log.info("downloadFileName:{}", downloadFileName);
-
-        if (StringUtils.isBlank(downloadDir)) {
-            throw new IOException("文件目录不能为空");
-        }
-        if (StringUtils.isBlank(downloadFileName)) {
-            throw new IOException("文件名称不能为空");
-        }
-        // 安全判断，防止../情况,防止出现类似非法文件名称：../../hello/123.txt
-        if (downloadFileName.contains(CommonConstant.SPOT_SPOT) || downloadFileName.contains(CommonConstant.SPOT_SPOT_BACKSLASH)) {
-            throw new IOException("非法的文件名称");
-        }
-        // 允许下载的文件后缀判断
-        if (CollectionUtils.isEmpty(allowFileExtensions)) {
-            throw new IllegalArgumentException("请设置允许下载的文件后缀");
-        }
-        // 获取文件名称
-        String fileExtension = FilenameUtils.getExtension(downloadFileName);
-
-        // 从服务器读取文件，然后输出
-        File downloadFile = new File(downloadDir, downloadFileName);
-        if (!downloadFile.exists()) {
-            throw new IOException("文件不存在");
-        }
-
-        // 判断文件类型，输出对应ContentType,如果没有对应的内容类型，可在config/mime-type.properties配置
-        String contentType = ContentTypeUtil.getContentType(downloadFile);
-        log.info("contentType:{}", contentType);
-        // 文件大小
-        long length = downloadFile.length();
-        log.info("length:{}", length);
-
-        // 下载回调处理
-        if (downloadHandler == null) {
-            // 使用默认下载处理器
-            downloadHandler = new DefaultDownloadHandler();
-        }
-        boolean flag = downloadHandler.handle(downloadDir, downloadFileName, downloadFile, fileExtension, contentType, length);
-        if (!flag) {
-            log.info("下载自定义校验失败，取消下载");
-            return;
-        }
-
-        // 下载文件名称编码，Firefox中文乱码处理
-        String encodeDownFileName;
-
-        HttpServletRequest request = HttpServletRequestUtil.getRequest();
-
-        String browser = BrowserUtil.getCurrent(request);
-        if (BrowserUtil.FIREFOX.equals(browser)) {
-            encodeDownFileName = "=?UTF-8?B?" + (new String(Base64Utils.encodeToString(downloadFileName.getBytes("UTF-8")))) + "?=";
-        } else {
-            encodeDownFileName = URLEncoder.encode(downloadFileName, "utf-8").replaceAll("\\+", "%20");
-        }
-        log.info("encodeDownFileName:{}", encodeDownFileName);
-
-        log.info("下载文件：" + downloadFile.getAbsolutePath());
-
-        response.reset();
-        // 设置Content-Disposition响应头
-        response.setHeader("Content-Disposition", "attachment;fileName=\"" + encodeDownFileName + "\"");
-        // 设置响应Content-Type
-        response.setContentType(contentType);
-        // 设置响应文件大小
-        response.setContentLengthLong(length);
-
-        // 文件下载
-        InputStream in = new BufferedInputStream(new FileInputStream(downloadFile));
-        FileCopyUtils.copy(in, response.getOutputStream());
-    }
-
-    public static interface DownloadHandler {
-        /**
-         * 下载自定义处理
-         *
-         * @param dir
-         * @param fileName
-         * @param file
-         * @param fileExtension
-         * @param contentType
-         * @param length
-         * @return
-         * @throws Exception
-         */
-        boolean handle(String dir, String fileName, File file, String fileExtension, String contentType, long length) throws Exception;
-    }
-
-    public static class DefaultDownloadHandler implements DownloadHandler {
-        @Override
-        public boolean handle(String dir, String fileName, File file, String fileExtension, String contentType, long length) throws Exception {
-            return false;
-        }
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/HttpServletRequestUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/HttpServletRequestUtil.java
deleted file mode 100644
index c1361403..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/HttpServletRequestUtil.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * 获取当前请求的HttpServletRequest对象
- * @author geekidea
- * @date 2018-11-08
- */
-public class HttpServletRequestUtil {
-
-    public static HttpServletRequest getRequest() {
-        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/HttpServletResponseUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/HttpServletResponseUtil.java
deleted file mode 100644
index 59815993..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/HttpServletResponseUtil.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import com.alibaba.fastjson.JSON;
-
-import javax.servlet.http.HttpServletResponse;
-import java.io.PrintWriter;
-
-/**
- * @author geekidea
- * @date 2018-11-08
- */
-public final class HttpServletResponseUtil {
-
-    private static String UTF8 = "UTF-8";
-    private static String CONTENT_TYPE = "application/json";
-
-    private HttpServletResponseUtil(){
-        throw new AssertionError();
-    }
-
-    public static void printJson(HttpServletResponse response, Object object) throws Exception{
-        response.setCharacterEncoding(UTF8);
-        response.setContentType(CONTENT_TYPE);
-        PrintWriter printWriter = response.getWriter();
-        printWriter.write(JSON.toJSONString(object));
-        printWriter.flush();
-        printWriter.close();
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/IniUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/IniUtil.java
deleted file mode 100644
index 842b9165..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/IniUtil.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import org.ini4j.Config;
-import org.ini4j.Ini;
-import org.ini4j.Profile;
-
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.Map;
-
-/**
- * @author geekidea
- * @date 2019-09-29
- * @since 1.3.0.RELEASE
- **/
-public class IniUtil {
-    public static Map<String,String> parseIni(String string) {
-        Config config = new Config();
-        config.setGlobalSection(true);
-        config.setGlobalSectionName("");
-        Ini ini = new Ini();
-        ini.setConfig(config);
-        try {
-            ini.load(new StringReader(string));
-            Profile.Section section = ini.get("");
-            return section;
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
-
-    public static Map<String,String> parseIni(String sectionName,String string) {
-        Ini ini = new Ini();
-        try {
-            ini.load(new StringReader(string));
-            Profile.Section section = ini.get(sectionName);
-            return section;
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/IpUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/IpUtil.java
deleted file mode 100644
index e58830ac..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/IpUtil.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
-
-import javax.servlet.http.HttpServletRequest;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * 获取IP地址工具类
- * @author geekidea
- * @date 2018-11-08
- */
-public final class IpUtil {
-
-    private static final String UNKNOWN = "unknown";
-    private static final String IPV6_LOCAL = "0:0:0:0:0:0:0:1";
-
-    private IpUtil(){
-        throw new AssertionError();
-    }
-
-    /**
-     * 获取请求用户的IP地址
-     * @return
-     */
-    public static String getRequestIp() {
-        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
-        HttpServletRequest request = attributes.getRequest();
-        return getRequestIp(request);
-    }
-
-    /**
-     * 获取请求用户的IP地址
-     * @param request
-     * @return
-     */
-    public static String getRequestIp(HttpServletRequest request) {
-        String ip = request.getHeader("x-forwarded-for");
-        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
-            ip = request.getHeader("Proxy-Client-IP");
-        }
-        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
-            ip = request.getHeader("WL-Proxy-Client-IP");
-        }
-        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
-            ip = request.getRemoteAddr();
-        }
-
-        if (IPV6_LOCAL.equals(ip)){
-            ip = getLocalhostIp();
-        }
-        return ip;
-    }
-
-    public static String getLocalhostIp(){
-        try {
-            return InetAddress.getLocalHost().getHostAddress();
-        } catch (UnknownHostException e) {
-        }
-        return null;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/Jackson.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/Jackson.java
deleted file mode 100644
index 820a483b..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/Jackson.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
-
-import java.util.TimeZone;
-
-/**
- * Jackson序列化工具类
- *
- * @author geekidea
- * @date 2019-11-01
- **/
-public class Jackson {
-
-    /**
-     * 时区
-     */
-    private static final TimeZone timeZone = TimeZone.getTimeZone("GMT+8");
-
-    /**
-     * 键按自然顺序输出
-     *
-     * @param object
-     * @return
-     */
-    public static String toJsonString(Object object) {
-        return toJsonString(object, false);
-    }
-
-    /**
-     * 键按自然顺序格式化输出
-     *
-     * @param object
-     * @param prettyFormat
-     * @return
-     */
-    public static String toJsonString(Object object, boolean prettyFormat) {
-        if (object == null) {
-            return null;
-        }
-        ObjectMapper objectMapper = new ObjectMapper();
-        try {
-            // 格式化输出
-            objectMapper.configure(SerializationFeature.INDENT_OUTPUT, prettyFormat);
-            // 键按自然顺序输出
-            objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
-            // 设置时区
-            objectMapper.setTimeZone(timeZone);
-            return objectMapper.writeValueAsString(object);
-        } catch (JsonProcessingException e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
-
-    /**
-     * 键按自然顺序格式化输出
-     *
-     * @param object
-     * @param prettyFormat
-     * @return
-     */
-    public static String toJsonStringNonNull(Object object) {
-        return toJsonStringNonNull(object, false);
-    }
-
-
-    /**
-     * 键按自然顺序格式化输出
-     *
-     * @param object
-     * @param prettyFormat
-     * @return
-     */
-    public static String toJsonStringNonNull(Object object, boolean prettyFormat) {
-        if (object == null) {
-            return null;
-        }
-        ObjectMapper objectMapper = new ObjectMapper();
-        try {
-            // 格式化输出
-            objectMapper.configure(SerializationFeature.INDENT_OUTPUT, prettyFormat);
-            // 键按自然顺序输出
-            objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
-            // 为空的序列化
-            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
-            // 设置时区
-            objectMapper.setTimeZone(timeZone);
-            return objectMapper.writeValueAsString(object);
-        } catch (JsonProcessingException e) {
-            e.printStackTrace();
-        }
-        return null;
-    }
-
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/LambdaColumn.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/LambdaColumn.java
deleted file mode 100644
index 1df3df9c..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/LambdaColumn.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
-import com.baomidou.mybatisplus.core.metadata.TableInfo;
-import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
-import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
-import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
-import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.MapUtils;
-
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * 根据lambda表达式获取数据库列名
- *
- * @author geekidea
- * @date 2019/12/22
- **/
-public class LambdaColumn<T> {
-
-    private static Map<String, Map<String, String>> ENTITY_COLUMN_MAP = new ConcurrentHashMap<>();
-
-    public static <T> LambdaColumn<T> lambda() {
-        return new LambdaColumn<>();
-    }
-
-    public String get(SFunction<T, ?> func) {
-        SerializedLambda lambda = LambdaUtils.resolve(func);
-        String methodName = lambda.getImplMethodName();
-        String methodPropertyName = null;
-        if (methodName.startsWith("get")) {
-            methodPropertyName = methodName.substring(3);
-            methodPropertyName = methodPropertyName.substring(0, 1).toLowerCase() + methodPropertyName.substring(1);
-
-        }
-        Class<?> cls = lambda.getImplClass();
-        String className = lambda.getImplClassName();
-        if (ENTITY_COLUMN_MAP.containsKey(className)) {
-            return ENTITY_COLUMN_MAP.get(className).get(methodPropertyName);
-        }
-
-        Map<String, String> map = getPropertyColumnMap(cls);
-        if (MapUtils.isNotEmpty(map)) {
-            ENTITY_COLUMN_MAP.put(className, map);
-        }
-        return ENTITY_COLUMN_MAP.get(className).get(methodPropertyName);
-    }
-
-    /**
-     * 从mybatisplus的TableInfo类中获取列名map信息
-     *
-     * @param cls
-     * @return
-     */
-    private Map<String, String> getPropertyColumnMap(Class<?> cls) {
-        TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
-        List<TableFieldInfo> tableFieldInfos = tableInfo.getFieldList();
-        if (CollectionUtils.isEmpty(tableFieldInfos)) {
-            return null;
-        }
-
-        Map<String, String> map = new ConcurrentHashMap<>();
-        String keyProperty = tableInfo.getKeyProperty();
-        String keyColumn = tableInfo.getKeyColumn();
-        map.put(keyProperty, keyColumn);
-
-        for (TableFieldInfo tableFieldInfo : tableFieldInfos) {
-            String column = tableFieldInfo.getColumn();
-            String property = tableFieldInfo.getProperty();
-            map.put(property, column);
-        }
-        return map;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/LoginUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/LoginUtil.java
deleted file mode 100644
index e781fb9d..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/LoginUtil.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import io.geekidea.springbootplus.config.constant.CommonRedisKey;
-import io.geekidea.springbootplus.framework.shiro.util.JwtTokenUtil;
-import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
-import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserRedisVo;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.stereotype.Component;
-
-
-/**
- * 获取登录信息工具类
- *
- * @author geekidea
- * @date 2018-11-08
- */
-@Slf4j
-@Component
-public class LoginUtil {
-
-    private static RedisTemplate redisTemplate;
-
-    public LoginUtil(RedisTemplate redisTemplate) {
-        LoginUtil.redisTemplate = redisTemplate;
-    }
-
-
-    /**
-     * 获取当前登录用户对象
-     *
-     * @return
-     */
-    public static LoginSysUserRedisVo getLoginSysUserRedisVo() {
-        // 获取当前登录用户
-        String token = JwtTokenUtil.getToken();
-        String username = JwtUtil.getUsername(token);
-        if (StringUtils.isBlank(username)) {
-            return null;
-        }
-        return (LoginSysUserRedisVo) redisTemplate.opsForValue().get(String.format(CommonRedisKey.LOGIN_USER, username));
-    }
-
-    /**
-     * 获取当前登录用户的ID
-     *
-     * @return
-     */
-    public static Long getUserId() {
-        LoginSysUserRedisVo loginSysUserRedisVo = getLoginSysUserRedisVo();
-        if (loginSysUserRedisVo == null) {
-            return null;
-        }
-        return loginSysUserRedisVo.getId();
-    }
-
-    /**
-     * 获取当前登录用户的账号
-     *
-     * @return
-     */
-    public static String getUsername() {
-        LoginSysUserRedisVo loginSysUserRedisVo = getLoginSysUserRedisVo();
-        if (loginSysUserRedisVo == null) {
-            return null;
-        }
-        return loginSysUserRedisVo.getUsername();
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/MapUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/MapUtil.java
deleted file mode 100644
index 10a2b3a6..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/MapUtil.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * <p>
- *  Map构建工具类
- * </p>
- * @author geekidea
- * @date 2019-05-23
- **/
-public class MapUtil {
-
-    private Map<String,Object> map;
-
-    private MapUtil(){
-        map = new HashMap<>();
-    }
-
-    public static MapUtil builder(){
-        return new MapUtil();
-    }
-
-    public MapUtil put(String key,Object value){
-        this.map.put(key,value);
-        return this;
-    }
-
-    public Map<String,Object> build(){
-        return this.map;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/PasswordUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/PasswordUtil.java
deleted file mode 100644
index 91e1f3df..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/PasswordUtil.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.lang3.StringUtils;
-
-/**
- * 密码加密工具类
- *
- * @author geekidea
- * @date 2018-11-08
- */
-@Slf4j
-public class PasswordUtil {
-
-    /**
-     * 密码加盐，再加密
-     *
-     * @param pwd
-     * @param salt
-     * @return
-     */
-    public static String encrypt(String pwd, String salt) {
-        if (StringUtils.isBlank(pwd)) {
-            throw new IllegalArgumentException("密码不能为空");
-        }
-        if (StringUtils.isBlank(salt)) {
-            throw new IllegalArgumentException("盐值不能为空");
-        }
-        return DigestUtils.sha256Hex(pwd + salt);
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/PhoneUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/PhoneUtil.java
deleted file mode 100644
index 47e52509..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/PhoneUtil.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
-
-/**
- * 手机号码工具类
- *
- * @author geekidea
- * @date 2020/2/26
- **/
-@Slf4j
-public class PhoneUtil {
-
-    /**
-     * 手机号码长度
-     */
-    private static final int PHONE_LENGTH = 11;
-
-    /**
-     * 脱敏*号
-     */
-    private static final String ASTERISK = "****";
-
-    /**
-     * 手机号码脱敏
-     * 截取手机号码前三位，后4为，中间4位使用*号代替
-     * 18812345678
-     * 188****5678
-     *
-     * @param phone
-     * @return
-     */
-    public static String desensitize(String phone) {
-        // 校验手机号码
-        if (StringUtils.isBlank(phone)) {
-            return null;
-        }
-        if (phone.length() != PHONE_LENGTH) {
-            log.error("手机号码不合法：" + phone);
-            return phone;
-        }
-
-        String before = phone.substring(0,3);
-        String after = phone.substring(7,11);
-        String desensitizePhone = before + "****" + after;
-        return desensitizePhone;
-    }
-
-    public static void main(String[] args) {
-        String phone = desensitize("1881234567");
-        System.out.println("phone = " + phone);
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/PrintApplicationInfo.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/PrintApplicationInfo.java
deleted file mode 100644
index 0958b1cb..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/PrintApplicationInfo.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import lombok.extern.slf4j.Slf4j;
-import org.fusesource.jansi.Ansi;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.core.env.ConfigurableEnvironment;
-
-/**
- * <p>
- * 打印项目信息
- * </p>
- *
- * @author geekidea
- * @date 2019-05-08
- **/
-@Slf4j
-public class PrintApplicationInfo {
-
-    /**
-     * 执行之前，打印前置条件提示
-     */
-    public static void printTip(ConfigurableApplicationContext context) {
-        ConfigurableEnvironment environment = context.getEnvironment();
-        // 项目profile
-        String profileActive = environment.getProperty("spring.profiles.active");
-        StringBuffer tip = new StringBuffer();
-        tip.append("===========================================================================================\n");
-        tip.append("                                                                                  \n");
-        tip.append("                               !!!准备工作!!!                                      \n");
-        tip.append(" 1.导入SQL初始化脚本：docs/db，根据不同数据库导入对应SQL脚本并修改链接等信息配置\n");
-        tip.append(" 2.启动Redis服务，必要条件\n");
-        tip.append(" 3.启动SpringBootAdmin Server，可选操作，admin模块中，启动SpringBootPlusAdminApplication\n");
-        tip.append(" 4.根据项目需要，修改项目配置，请先查看官网配置文档：https://springboot.plus/config/\n");
-        tip.append(" 5.项目模块说明：\n");
-        tip.append("    admin：       SpringBootAdmin Server启动模块\n");
-        tip.append("    bootstrap：   项目启动模块\n");
-        tip.append("    config：      项目配置模块\n");
-        tip.append("    distribution：项目打包模块，打包时，请先选中Maven Profiles中的release和对应环境\n");
-        tip.append("    example：     业务自定义模块，自己的业务代码可在example下进行，也可以再创建模块\n");
-        tip.append("    framework：   项目核心框架模块\n");
-        tip.append("    generator：   代码生成模块，启动类：SpringBootPlusGenerator，请根据实际情况进行配置\n");
-        tip.append("    scheduled：   任务调度模块\n");
-        tip.append("    system：      系统管理模块\n");
-        tip.append(" 6.FAQ：https://springboot.plus/faq\n");
-        tip.append(" 7.如开发中遇到bug及问题，欢迎提交ISSUES：https://github.com/geekidea/spring-boot-plus/issues\n");
-        tip.append(" 8.QQ：625301326，进群答案：springboot.plus\n");
-        tip.append("                                                                                  \n");
-        tip.append("===========================================================================================\n");
-        if ("dev".equals(profileActive)) {
-            log.info("\n{}", Ansi.ansi().eraseScreen().fg(Ansi.Color.YELLOW).a(tip.toString()).reset().toString());
-        }
-    }
-
-    /**
-     * 启动成功之后，打印项目信息
-     */
-    public static void print(ConfigurableApplicationContext context) {
-        ConfigurableEnvironment environment = context.getEnvironment();
-        // 项目名称
-        String projectFinalName = environment.getProperty("info.project-finalName");
-        // 项目版本
-        String projectVersion = environment.getProperty("info.project-version");
-        // 项目profile
-        String profileActive = environment.getProperty("spring.profiles.active");
-        // 项目路径
-        String contextPath = environment.getProperty("server.servlet.context-path");
-        // 项目IP或域名地址
-        String serverIp = environment.getProperty("spring-boot-plus.server-ip");
-        // 项目端口
-        String port = environment.getProperty("server.port");
-        // Spring Boot Admin Server地址，请先在admin模块中启动 SpringBootPlusAdminApplication
-        String springBootAdminServerUrl = environment.getProperty("spring.boot.admin.client.url");
-
-        log.info("projectFinalName : {}", projectFinalName);
-        log.info("projectVersion : {}", projectVersion);
-        log.info("profileActive : {}", profileActive);
-        log.info("contextPath : {}", contextPath);
-        log.info("serverIp : {}", serverIp);
-        log.info("port : {}", port);
-
-        String startSuccess = " ____    __                    __        ____                                                   \n" +
-                "/\\  _`\\ /\\ \\__                /\\ \\__    /\\  _`\\                                                 \n" +
-                "\\ \\,\\L\\_\\ \\ ,_\\    __     _ __\\ \\ ,_\\   \\ \\,\\L\\_\\  __  __    ___    ___     __    ____    ____  \n" +
-                " \\/_\\__ \\\\ \\ \\/  /'__`\\  /\\`'__\\ \\ \\/    \\/_\\__ \\ /\\ \\/\\ \\  /'___\\ /'___\\ /'__`\\ /',__\\  /',__\\ \n" +
-                "   /\\ \\L\\ \\ \\ \\_/\\ \\L\\.\\_\\ \\ \\/ \\ \\ \\_     /\\ \\L\\ \\ \\ \\_\\ \\/\\ \\__//\\ \\__//\\  __//\\__, `\\/\\__, `\\\n" +
-                "   \\ `\\____\\ \\__\\ \\__/.\\_\\\\ \\_\\  \\ \\__\\    \\ `\\____\\ \\____/\\ \\____\\ \\____\\ \\____\\/\\____/\\/\\____/\n" +
-                "    \\/_____/\\/__/\\/__/\\/_/ \\/_/   \\/__/     \\/_____/\\/___/  \\/____/\\/____/\\/____/\\/___/  \\/___/ \n" +
-                "                                                                                                \n" +
-                "                                                                                                ";
-
-        String homeUrl = "http://" + serverIp + ":" + port + contextPath;
-        String swaggerUrl = "http://" + serverIp + ":" + port + contextPath + "/swagger-ui.html";
-        String knife4jUrl = "http://" + serverIp + ":" + port + contextPath + "/doc.html";
-        log.info("Admin:   {}", springBootAdminServerUrl);
-        log.info("Home:    {}", homeUrl);
-        log.info("Knife4j: {}", knife4jUrl);
-        log.info("Swagger: {}", swaggerUrl);
-        log.info("spring-boot-plus project start success...........");
-        if ("dev".equals(profileActive)) {
-            log.info("\n{}", AnsiUtil.getAnsi(Ansi.Color.BLUE, startSuccess));
-        } else {
-            log.info("\n{}", startSuccess);
-        }
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/PropertyColumnUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/PropertyColumnUtil.java
deleted file mode 100644
index 6296585d..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/PropertyColumnUtil.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
-import com.baomidou.mybatisplus.core.metadata.TableInfo;
-import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.MapUtils;
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-
-/**
- * @author geekidea
- * @date 2020/3/2
- **/
-public class PropertyColumnUtil {
-
-    private static Map<Class<?>, Map<String, String>> cacheMap = new ConcurrentHashMap<>();
-
-    public static Map<Class<?>, Map<String, String>> getMap() {
-        return cacheMap;
-    }
-
-    /**
-     * 根据实体class，从mybatisplus中获取对应Table的属性列名Map
-     *
-     * @param clazz
-     * @return
-     */
-    private static Map<String, String> getTableFieldMap(Class<?> clazz) {
-        TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);
-        if (tableInfo == null) {
-            return null;
-        }
-        List<TableFieldInfo> tableFieldInfos = tableInfo.getFieldList();
-        if (CollectionUtils.isEmpty(tableFieldInfos)) {
-            return null;
-        }
-        Map<String, String> cacheMap = tableFieldInfos.stream().collect(Collectors.toMap(TableFieldInfo::getProperty, TableFieldInfo::getColumn));
-        return cacheMap;
-    }
-
-    /**
-     * 从本地缓存中获取属性列名map
-     *
-     * @param clazz
-     * @return
-     */
-    public static Map<String, String> getPropertyColumnMap(Class<?> clazz) {
-        Map<String, String> propertyColumnMap = cacheMap.get(clazz);
-        if (MapUtils.isEmpty(propertyColumnMap)) {
-            // 从TableInfo中获取，并缓存到内存map中
-            Map<String, String> fieldMap = getTableFieldMap(clazz);
-            if (MapUtils.isEmpty(fieldMap)) {
-                return null;
-            } else {
-                cacheMap.put(clazz, fieldMap);
-                return fieldMap;
-            }
-        } else {
-            return propertyColumnMap;
-        }
-    }
-
-    /**
-     * 通过实体class类型和属性名称，从缓存中获取对应的列名
-     *
-     * @param clazz
-     * @param property
-     * @return
-     */
-    public static String getColumn(Class<?> clazz, String property) {
-        Map<String, String> propertyColumnMap = getPropertyColumnMap(clazz);
-        if (MapUtils.isEmpty(propertyColumnMap)) {
-            throw new IllegalArgumentException("没有找到对应的实体映射对象");
-        }
-        String column = propertyColumnMap.get(property);
-        if (StringUtils.isEmpty(column)) {
-            throw new IllegalArgumentException("没有找到对应的列");
-        }
-        return column;
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/RedisCacheUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/RedisCacheUtil.java
deleted file mode 100644
index a0bcf7b2..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/RedisCacheUtil.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.PostConstruct;
-
-/**
- * @author geekidea
- * @date 2018-11-08
- */
-@Component
-@Slf4j
-public class RedisCacheUtil {
-
-    private static RedisCacheUtil redisCacheUtil;
-
-    @Autowired
-    private RedisTemplate redisTemplate;
-
-    /**
-     * 将当前对象赋值给静态对象,调用spring组件: redisCacheUtil.redisTemplate.xxx()
-     */
-    @PostConstruct
-    public void init(){
-        redisCacheUtil = this;
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/SpringContextUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/SpringContextUtil.java
deleted file mode 100644
index 97cf8cae..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/SpringContextUtil.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import org.springframework.beans.BeansException;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.springframework.stereotype.Component;
-
-/**
- * Spring工具类,获取Spring上下文对象等
- * @author geekidea
- * @date 2018-11-08
- */
-@Component
-public class SpringContextUtil implements ApplicationContextAware {
-
-    private static ApplicationContext applicationContext = null;
-
-    @Override
-    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
-        if(SpringContextUtil.applicationContext == null){
-            SpringContextUtil.applicationContext  = applicationContext;
-        }
-    }
-
-    public static ApplicationContext getApplicationContext() {
-        return applicationContext;
-    }
-
-    public static Object getBean(String name){
-        return getApplicationContext().getBean(name);
-    }
-
-    public static <T> T getBean(Class<T> clazz){
-        return getApplicationContext().getBean(clazz);
-    }
-
-    public static <T> T getBean(String name,Class<T> clazz){
-        return getApplicationContext().getBean(name, clazz);
-    }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/TokenUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/TokenUtil.java
deleted file mode 100644
index 4c9e25ea..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/TokenUtil.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-/**
- * @author geekidea
- * @date 2018-11-08
- */
-public class TokenUtil {
-
-    /**
-     *
-     * @return
-     */
-    public static String generateFirstLoginRestPwdToken(){
-        String token = "first-login-rest-pwd-token:" + UUIDUtil.getUuid();
-        return token;
-    }
-
-    /**
-     * 生成验证码token
-     * @return
-     */
-    public static String generateVerificationCodeToken(){
-        String token = "verification-code-token:" + UUIDUtil.getUuid();
-        return token;
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/UUIDUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/UUIDUtil.java
deleted file mode 100644
index 15046bb5..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/UUIDUtil.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import java.util.UUID;
-
-/**
- * @author geekidea
- * @date 2018-11-08
- */
-public class UUIDUtil {
-
-    public static String getUuid(){
-        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
-        return uuid;
-    }
-    
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/UploadUtil.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/UploadUtil.java
deleted file mode 100644
index 61332be9..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/UploadUtil.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.FilenameUtils;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.io.File;
-import java.io.InputStream;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-
-/**
- * 文件上传工具类
- *
- * @author geekidea
- * @date 2019/8/21
- * @since 1.2.1-RELEASE
- */
-@Slf4j
-public final class UploadUtil {
-
-    /**
-     * 上传文件，默认文件名格式，yyyyMMddHHmmssS
-     *
-     * @param uploadPath
-     * @param multipartFile
-     * @return
-     * @throws Exception
-     */
-    public static String upload(String uploadPath, MultipartFile multipartFile) throws Exception {
-        return upload(uploadPath, multipartFile, new DefaultUploadFileNameHandleImpl());
-    }
-
-    /**
-     * 上传文件
-     *
-     * @param uploadPath           上传目录
-     * @param multipartFile        上传文件
-     * @param uploadFileNameHandle 回调
-     * @return
-     * @throws Exception
-     */
-    public static String upload(String uploadPath, MultipartFile multipartFile, UploadFileNameHandle uploadFileNameHandle) throws Exception {
-        // 获取输入流
-        InputStream inputStream = multipartFile.getInputStream();
-        // 文件保存目录
-        File saveDir = new File(uploadPath);
-        // 判断目录是否存在，不存在，则创建，如创建失败，则抛出异常
-        if (!saveDir.exists()) {
-            boolean flag = saveDir.mkdirs();
-            if (!flag) {
-                throw new RuntimeException("创建" + saveDir + "目录失败！");
-            }
-        }
-
-        String originalFilename = multipartFile.getOriginalFilename();
-        String saveFileName;
-        if (uploadFileNameHandle == null) {
-            saveFileName = new DefaultUploadFileNameHandleImpl().handle(originalFilename);
-        } else {
-            saveFileName = uploadFileNameHandle.handle(originalFilename);
-        }
-        log.info("saveFileName = " + saveFileName);
-
-        File saveFile = new File(saveDir, saveFileName);
-        // 保存文件到服务器指定路径
-        FileUtils.copyToFile(inputStream, saveFile);
-        return saveFileName;
-    }
-
-    /**
-     * 删除删除的文件
-     *
-     * @param uploadPath
-     * @param saveFileName
-     */
-    public static void deleteQuietly(String uploadPath, String saveFileName) {
-        File saveDir = new File(uploadPath);
-        File saveFile = new File(saveDir, saveFileName);
-        log.debug("删除文件：" + saveFile);
-        FileUtils.deleteQuietly(saveFile);
-
-    }
-
-
-    public static interface UploadFileNameHandle {
-        /**
-         * 回调处理接口
-         *
-         * @param originalFilename
-         * @return
-         */
-        String handle(String originalFilename);
-    }
-
-    public static class DefaultUploadFileNameHandleImpl implements UploadFileNameHandle {
-
-        @Override
-        public String handle(String originalFilename) {
-            // 文件后缀
-            String fileExtension = FilenameUtils.getExtension(originalFilename);
-            // 这里可自定义文件名称，比如按照业务类型/文件格式/日期
-            // 此处按照文件日期存储
-            String dateString = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssS"));
-            String fileName = dateString + "." + fileExtension;
-            return fileName;
-        }
-    }
-
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/VerificationCode.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/VerificationCode.java
deleted file mode 100644
index cd53e8f9..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/VerificationCode.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.util;
-
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.util.Random;
-
-/**
- * @author geekidea
- * @date 2018-11-08
- */
-public class VerificationCode {
-    /**
-     * 验证码图片的长
-     **/
-    private int weight = 110;
-    /**
-     * 验证码图片的高
-     */
-    private int height = 38;
-    /**
-     * 用来保存验证码的文本内容
-     **/
-    private String text;
-    /**
-     * 获取随机数对象
-     **/
-    private Random r = new Random();
-    /**
-     * 字体数组
-     **/
-    private String[] fontNames = {"宋体", "华文楷体", "黑体", "微软雅黑", "楷体_GB2312"};
-    /**
-     * 验证码数组
-     **/
-    private String codes = "23456789acdefghjkmnopqrstuvwxyzACDEFGHJKMNPQRSTUVWXYZ";
-    /**
-     * 生成的验证码的个数
-     **/
-    private int codeNum = 4;
-    /**
-     * 255
-     **/
-    private static int TWO_FIVE_FIVE = 255;
-
-    /**
-     * 获取随机的颜色
-     */
-    private Color randomColor() {
-        //这里为什么是150，因为当r，g，b都为255时，即为白色，为了好辨认，需要颜色深一点。
-        int r = this.r.nextInt(150);
-        int g = this.r.nextInt(150);
-        int b = this.r.nextInt(150);
-        //返回一个随机颜色
-        return new Color(r, g, b);
-    }
-
-    /**
-     * 获取随机字体
-     */
-    private Font randomFont() {
-        //获取随机的字体
-        int index = r.nextInt(fontNames.length);
-        String fontName = fontNames[index];
-        //随机获取字体的样式，0是无样式，1是加粗，2是斜体，3是加粗加斜体
-        int style = r.nextInt(4);
-        //随机获取字体的大小
-        int size = r.nextInt(5) + 24;
-        //返回一个随机的字体
-        return new Font(fontName, style, size);
-    }
-
-    /**
-     * 获取随机字符
-     */
-    private char randomChar() {
-        int index = r.nextInt(codes.length());
-        return codes.charAt(index);
-    }
-
-    /**
-     * 画干扰线，验证码干扰线用来防止计算机解析图片
-     */
-    private void drawLine(BufferedImage image) {
-        int num = 155;
-        //定义干扰线的数量
-        Graphics2D g = (Graphics2D) image.getGraphics();
-        for (int i = 0; i < num; i++) {
-            int x = r.nextInt(weight);
-            int y = r.nextInt(height);
-            int xl = r.nextInt(weight);
-            int yl = r.nextInt(height);
-            g.setColor(getRandColor(160, 200));
-            g.drawLine(x, y, x + xl, y + yl);
-        }
-    }
-
-    /**
-     * 创建图片的方法
-     */
-    private BufferedImage createImage() {
-        //创建图片缓冲区
-        BufferedImage image = new BufferedImage(weight, height, BufferedImage.TYPE_INT_RGB);
-        //获取画笔
-        Graphics2D g = (Graphics2D) image.getGraphics();
-        // 设定图像背景色(因为是做背景，所以偏淡)
-        g.setColor(getRandColor(200, 250));
-        g.fillRect(0, 0, weight, height);
-        //返回一个图片
-        return image;
-    }
-
-    /**
-     * 获取验证码图片的方法
-     */
-    public BufferedImage getImage() {
-        BufferedImage image = createImage();
-        //获取画笔
-        Graphics2D g = (Graphics2D) image.getGraphics();
-        StringBuilder sb = new StringBuilder();
-        drawLine(image);
-        //画四个字符即可
-        for (int i = 0; i < codeNum; i++) {
-            //随机生成字符，因为只有画字符串的方法，没有画字符的方法，所以需要将字符变成字符串再画
-            String s = randomChar() + "";
-            //添加到StringBuilder里面
-            sb.append(s);
-            //定义字符的x坐标
-            float x = i * 1.0F * weight / 4;
-            //设置字体，随机
-            g.setFont(randomFont());
-            //设置颜色，随机
-            g.setColor(randomColor());
-            g.drawString(s, x, height - 5);
-        }
-        this.text = sb.toString();
-        return image;
-    }
-
-    /**
-     * 给定范围获得随机颜色
-     */
-    Color getRandColor(int fc, int bc) {
-        Random random = new Random();
-        if (fc > TWO_FIVE_FIVE) {
-            fc = TWO_FIVE_FIVE;
-        }
-
-        if (bc > TWO_FIVE_FIVE) {
-            bc = TWO_FIVE_FIVE;
-        }
-
-        int r = fc + random.nextInt(bc - fc);
-        int g = fc + random.nextInt(bc - fc);
-        int b = fc + random.nextInt(bc - fc);
-        return new Color(r, g, b);
-    }
-
-    /**
-     * 获取验证码文本的方法
-     */
-    public String getText() {
-        return text;
-    }
-}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/util/package-info.java b/framework/src/main/java/io/geekidea/springbootplus/framework/util/package-info.java
deleted file mode 100644
index 624963ce..00000000
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/util/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-/**
- * 公共工具包
- * @author geekidea
- * @date 2018-11-08
- */
-package io.geekidea.springbootplus.framework.util;
\ No newline at end of file
diff --git a/framework/src/main/resources/mapper/ip/IpAddressMapper.xml b/framework/src/main/resources/mapper/ip/IpAddressMapper.xml
deleted file mode 100644
index b3aab33c..00000000
--- a/framework/src/main/resources/mapper/ip/IpAddressMapper.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="io.geekidea.springbootplus.framework.ip.mapper.IpAddressMapper">
-
-    <select id="getByIp" resultType="io.geekidea.springbootplus.framework.ip.entity.IpAddress">
-        select area, operator
-        from ip_address
-        where INET_ATON(#{ip}) > ip_start_num
-          and INET_ATON(#{ip}) <![CDATA[ < ]]> ip_end_num
-    </select>
-
-</mapper>
diff --git a/framework/src/main/resources/mapper/log/SysLoginLogMapper.xml b/framework/src/main/resources/mapper/log/SysLoginLogMapper.xml
deleted file mode 100644
index d461b371..00000000
--- a/framework/src/main/resources/mapper/log/SysLoginLogMapper.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="io.geekidea.springbootplus.framework.log.mapper.SysLoginLogMapper">
-
-</mapper>
diff --git a/framework/src/main/resources/mapper/log/SysOperationLogMapper.xml b/framework/src/main/resources/mapper/log/SysOperationLogMapper.xml
deleted file mode 100644
index 3a1a3381..00000000
--- a/framework/src/main/resources/mapper/log/SysOperationLogMapper.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="io.geekidea.springbootplus.framework.log.mapper.SysOperationLogMapper">
-
-</mapper>
diff --git a/generator/.gitignore b/generator/.gitignore
deleted file mode 100644
index ccdf702f..00000000
--- a/generator/.gitignore
+++ /dev/null
@@ -1,36 +0,0 @@
-/target/
-/classes
-!.mvn/wrapper/maven-wrapper.jar
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-
-### NetBeans ###
-/nbproject/private/
-/build/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-
-.DS_Store
-
-*.log
-logs
-
-*.rdb
-
-
-
diff --git a/generator/README.md b/generator/README.md
deleted file mode 100644
index ebdac6f5..00000000
--- a/generator/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# generator 项目代码生成模块
\ No newline at end of file
diff --git a/generator/pom.xml b/generator/pom.xml
deleted file mode 100644
index 47f9ebb4..00000000
--- a/generator/pom.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>io.geekidea.springbootplus</groupId>
-        <artifactId>parent</artifactId>
-        <version>2.0</version>
-    </parent>
-
-    <artifactId>generator</artifactId>
-    <name>generator</name>
-    <description>代码生成器模块</description>
-
-    <dependencies>
-        <dependency>
-            <groupId>io.geekidea.springbootplus</groupId>
-            <artifactId>framework</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.velocity</groupId>
-            <artifactId>velocity-engine-core</artifactId>
-            <version>${velocity.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-test</artifactId>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>com.baomidou</groupId>
-            <artifactId>mybatis-plus-generator</artifactId>
-            <version>${mybatis-plus-boot-starter.version}</version>
-        </dependency>
-    </dependencies>
-
-</project>
diff --git a/generator/src/main/java/io/geekidea/springbootplus/generator/CodeGenerator.java b/generator/src/main/java/io/geekidea/springbootplus/generator/CodeGenerator.java
deleted file mode 100644
index 1c0b29a1..00000000
--- a/generator/src/main/java/io/geekidea/springbootplus/generator/CodeGenerator.java
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator;
-
-import com.baomidou.mybatisplus.annotation.DbType;
-import com.baomidou.mybatisplus.core.toolkit.StringPool;
-import com.baomidou.mybatisplus.generator.AutoGenerator;
-import com.baomidou.mybatisplus.generator.InjectionConfig;
-import com.baomidou.mybatisplus.generator.config.*;
-import com.baomidou.mybatisplus.generator.config.po.TableInfo;
-import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
-import io.geekidea.springbootplus.generator.config.*;
-import io.geekidea.springbootplus.generator.config.query.SpringBootPlusMySqlQuery;
-import io.geekidea.springbootplus.generator.config.query.SpringBootPlusSqlServerQuery;
-import io.geekidea.springbootplus.generator.constant.GeneratorConstant;
-import io.geekidea.springbootplus.generator.exception.GeneratorException;
-import io.geekidea.springbootplus.generator.properties.GeneratorProperties;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.stereotype.Component;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-
-/**
- * spring-boot-plus代码生成器
- *
- * @author geekidea
- * @date 2018-11-08
- */
-@Slf4j
-@Data
-@Component
-@Accessors(chain = true)
-public class CodeGenerator {
-
-    public void generator(GeneratorProperties generatorProperties) {
-        if (generatorProperties == null) {
-            throw new GeneratorException("generatorProperties不能为空");
-        }
-        List<TableConfig> tableConfig = generatorProperties.getTableConfig();
-        if (CollectionUtils.isEmpty(tableConfig)) {
-            throw new GeneratorException("tableConfig不能为空");
-        }
-
-        // 循环生成
-        for (TableConfig generatorTable : tableConfig) {
-            String tableName = generatorTable.getTableName();
-            String pkIdName = generatorTable.getPkIdName();
-            // 生成代码
-            generator(tableName, pkIdName, generatorProperties);
-        }
-    }
-
-    /**
-     * 初始化变量
-     */
-    public void init(String tableName, GeneratorProperties generatorProperties) {
-        String parentPackage = generatorProperties.getParentPackage();
-        if (StringUtils.isBlank(parentPackage)) {
-            throw new GeneratorException("项目父包不能为空");
-        }
-        // 如果包路径为空，转换包名称路径
-        if (StringUtils.isNotBlank(parentPackage)) {
-            generatorProperties.setParentPackagePath(parentPackage.replaceAll("\\.", Matcher.quoteReplacement(File.separator)));
-        }
-
-        String mavenModuleName = generatorProperties.getMavenModuleName();
-        if (StringUtils.isBlank(mavenModuleName)) {
-            throw new GeneratorException("mavenModuleName不能为空");
-        }
-        if (StringUtils.isBlank(tableName)) {
-            throw new GeneratorException("tableName不能为空");
-        }
-
-        // 如果是单表操作，不生成QueryVo
-        GeneratorConfig generatorConfig = generatorProperties.getGeneratorConfig();
-        if (GeneratorStrategy.SINGLE == generatorConfig.generatorStrategy) {
-            generatorProperties.getGeneratorConfig().setGeneratorQueryVo(false);
-        }
-
-    }
-
-
-    /**
-     * 生成代码
-     */
-    public void generator(String tableName, String pkIdName, GeneratorProperties generatorProperties) {
-        // 初始化变量
-        init(tableName, generatorProperties);
-
-        // 代码生成器
-        AutoGenerator autoGenerator = new AutoGenerator();
-
-        MybatisPlusGeneratorConfig mybatisPlusGeneratorConfig = generatorProperties.getMybatisPlusGeneratorConfig();
-        if (mybatisPlusGeneratorConfig == null) {
-            throw new GeneratorException("mybatisPlusGeneratorConfig不能为空");
-        }
-
-        // 全局配置
-        GlobalConfig globalConfig = mybatisPlusGeneratorConfig.getGlobalConfig();
-        if (globalConfig == null) {
-            throw new GeneratorException("globalConfig不能为空");
-        }
-
-        String mavenModuleName = generatorProperties.getMavenModuleName();
-        String moduleName = generatorProperties.getModuleName();
-        String outputDir = generatorProperties.getOutputDir();
-        String javaOutputDir = null;
-        String resourcesOutputDir = null;
-        String mapperXmlOutputDir = null;
-
-        if (StringUtils.isBlank(outputDir)) {
-            String rootProjectFile = System.getProperty(GeneratorConstant.USER_DIR);
-            outputDir = rootProjectFile + File.separator + mavenModuleName;
-        }
-        log.info("outputDir: {}", outputDir);
-        javaOutputDir = outputDir + GeneratorConstant.JAVA_DIR;
-        resourcesOutputDir = outputDir + GeneratorConstant.RESOURCES_DIR;
-        mapperXmlOutputDir = outputDir + GeneratorConstant.MAPPER_DIR;
-
-        // 设置java代码输出目录绝对路径
-        globalConfig.setOutputDir(javaOutputDir);
-
-        // 是否覆盖文件
-        globalConfig.setFileOverride(generatorProperties.isFileOverride());
-
-        // 设置作者
-        globalConfig.setAuthor(generatorProperties.getAuthor());
-
-        if (StringUtils.isBlank(globalConfig.getServiceName())) {
-            //  %sService 生成 UserService,%s 为占位符
-            globalConfig.setServiceName(GeneratorConstant.SERVICE_NAME);
-        }
-
-        // 设置全局配置
-        autoGenerator.setGlobalConfig(globalConfig);
-
-        // 数据源配置
-        DataSourceConfig dataSourceConfig = generatorProperties.getDataSourceConfig();
-        if (dataSourceConfig == null) {
-            throw new GeneratorException("dataSourceConfig不能为空");
-        }
-
-        // 设置元数据自定义查询
-        DbType dbType = dataSourceConfig.getDbType();
-        if (DbType.MYSQL == dbType) {
-            // 设置MySQL元数据自定义查询
-            dataSourceConfig.setDbQuery(new SpringBootPlusMySqlQuery());
-        } else if (DbType.SQL_SERVER2005 == dbType || DbType.SQL_SERVER == dbType) {
-            // 设置SQLServer元数据自定义查询
-            dataSourceConfig.setDbQuery(new SpringBootPlusSqlServerQuery());
-        }
-
-        // 设置数据源
-        autoGenerator.setDataSource(dataSourceConfig);
-
-        // 包配置
-        PackageConfig packageConfig = mybatisPlusGeneratorConfig.getPackageConfig();
-        packageConfig.setModuleName(moduleName);
-        packageConfig.setParent(generatorProperties.getParentPackage());
-
-        // 设置包信息
-        autoGenerator.setPackageInfo(packageConfig);
-
-        // 策略配置
-        StrategyConfig strategyConfig = mybatisPlusGeneratorConfig.getStrategyConfig();
-        if (strategyConfig == null) {
-            throw new GeneratorException("strategyConfig不能为空");
-        }
-        // 设置生成的表
-        strategyConfig.setInclude(tableName);
-        // 表前缀
-        List<String> tablePrefixList = generatorProperties.getTablePrefix();
-        if (CollectionUtils.isNotEmpty(tablePrefixList)) {
-            strategyConfig.setTablePrefix(tablePrefixList.toArray(new String[]{}));
-        }
-
-
-        if (StringUtils.isBlank(strategyConfig.getSuperEntityClass())) {
-            // 自定义继承的Entity类全称，带包名
-            strategyConfig.setSuperEntityClass(GeneratorConstant.SUPER_ENTITY_CLASS);
-        }
-        if (StringUtils.isBlank(strategyConfig.getSuperControllerClass())) {
-            // 自定义继承的Controller类全称，带包名
-            strategyConfig.setSuperControllerClass(GeneratorConstant.SUPER_CONTROLLER_CLASS);
-        }
-        // 自定义继承的Service类全称，带包名
-        strategyConfig.setSuperServiceClass(GeneratorConstant.SUPER_SERVICE_CLASS);
-        // 自定义继承的ServiceImpl类全称，带包名
-        strategyConfig.setSuperServiceImplClass(GeneratorConstant.SUPER_SERVICE_IMPL_CLASS);
-
-        // 设置策略
-        autoGenerator.setStrategy(strategyConfig);
-
-        // 自定义配置
-        InjectionConfig injectionConfig = new InjectionConfig() {
-            @Override
-            public void initMap() {
-                List<TableInfo> tableInfos = this.getConfig().getTableInfoList();
-                if (CollectionUtils.isEmpty(tableInfos)) {
-                    throw new GeneratorException(tableName + "表不存在");
-                }
-
-                String finalModuleName = "";
-                if (StringUtils.isNotBlank(moduleName)) {
-                    finalModuleName = StringPool.DOT + moduleName;
-                }
-                TableInfo tableInfo = this.getConfig().getTableInfoList().get(0);
-                String entityName = tableInfo.getEntityName();
-                String entityObjectName = toCamel(entityName);
-                String colonTableName = underlineToColon(tableName);
-
-                String parentPackage = generatorProperties.getParentPackage();
-                ProjectConfig projectConfig = generatorProperties.getProjectConfig();
-
-                String superPageParamClass = projectConfig.getSuperPageParamClass();
-                String idParamClass = projectConfig.getIdParamClass();
-                String pagingClass = projectConfig.getPagingClass();
-                String pageInfoClass = projectConfig.getPageInfoClass();
-                String apiResultClass = projectConfig.getApiResultClass();
-                String superPageOrderParamClass = projectConfig.getSuperPageOrderParamClass();
-
-                GeneratorConfig generatorConfig = generatorProperties.getGeneratorConfig();
-
-                GeneratorStrategy generatorStrategy = generatorConfig.getGeneratorStrategy();
-                String generatorStrategyName = generatorStrategy.name();
-                boolean paramValidation = generatorConfig.isParamValidation();
-                boolean requiresPermissions = generatorConfig.isRequiresPermissions();
-
-                Map<String, Object> map = new HashMap<>();
-                // 查询参数包路径
-                String pageParamPackage = parentPackage + finalModuleName + StringPool.DOT + GeneratorConstant.PARAM;
-                map.put("pageParamPackage", pageParamPackage);
-                // 查询参数类路径
-                map.put("pageParamClass", pageParamPackage + StringPool.DOT + entityName + GeneratorConstant.PAGE_PARAM);
-                // 查询参数共公包路径
-                map.put("superPageParamClass", superPageParamClass);
-                map.put("superPageOrderParamClass", superPageOrderParamClass);
-                // 查询参数共公包路径
-                map.put("idParamClass", idParamClass);
-                // 响应结果包路径
-                String queryVoPackage = parentPackage + finalModuleName + StringPool.DOT + GeneratorConstant.VO;
-                map.put("queryVoPackage", queryVoPackage);
-                // 响应结果类路径
-                map.put("queryVoClass", queryVoPackage + StringPool.DOT + entityName + GeneratorConstant.QUERY_VO);
-                // 实体对象名称
-                map.put("entityObjectName", entityObjectName);
-                // service对象名称
-                map.put("serviceObjectName", entityObjectName + GeneratorConstant.SERVICE);
-                // mapper对象名称
-                map.put("mapperObjectName", entityObjectName + GeneratorConstant.MAPPER);
-                // 主键ID列名
-                map.put("pkIdName", pkIdName);
-                // 主键ID驼峰名称
-                map.put("pkIdCamelName", underlineToCamel(pkIdName));
-                // 导入分页类
-                map.put("pagingClass", pagingClass);
-                map.put("pageInfoClass", pageInfoClass);
-                // ApiResult
-                map.put("apiResultClass", apiResultClass);
-                // 代码生成策略
-                map.put("generatorStrategy", generatorStrategyName);
-                // 代码Validation校验
-                map.put("paramValidation", paramValidation);
-                // 冒号连接的表名称
-                map.put("colonTableName", colonTableName);
-                // 是否生成Shiro RequiresPermissions注解
-                map.put("requiresPermissions", requiresPermissions);
-                // 设置常量值
-                map.put("pageParam", GeneratorConstant.PAGE_PARAM);
-                map.put("validatorAddPackage", GeneratorConstant.VALIDATOR_ADD_PACKAGE);
-                map.put("validatorUpdatePackage", GeneratorConstant.VALIDATOR_UPDATE_PACKAGE);
-                // 如果是但表模式，queryVO值为null
-                if (GeneratorStrategy.SINGLE == generatorStrategy) {
-                    map.put("queryVo", "");
-                } else {
-                    map.put("queryVo", GeneratorConstant.QUERY_VO);
-                }
-                map.put("generatorQueryVo", generatorConfig.isGeneratorQueryVo());
-                map.put("generatorPageParam", generatorConfig.isGeneratorPageParam());
-                map.put("pageListOrder", generatorConfig.isPageListOrder());
-                map.put("swaggerTags", generatorConfig.isSwaggerTags());
-                map.put("operationLog", generatorConfig.isOperationLog());
-                map.put("module", moduleName);
-                this.setMap(map);
-            }
-        };
-        List<FileOutConfig> focList = new ArrayList<>();
-
-
-        GeneratorConfig generatorConfig = generatorProperties.getGeneratorConfig();
-
-        String parentPackagePath = generatorProperties.getParentPackagePath();
-        String finalJavaOutputDir = javaOutputDir;
-        String finalMapperXmlOutputDir = mapperXmlOutputDir;
-
-        String modulePath = "";
-        if (StringUtils.isNotBlank(moduleName)) {
-            modulePath = File.separator + moduleName;
-        }
-        final String finalModulePath = modulePath;
-
-        // 生成Mapper XML
-        if (generatorConfig.isGeneratorMapperXml()) {
-            focList.add(new FileOutConfig(GeneratorConstant.MAPPER_XML_TEMPLATE_PATH) {
-                @Override
-                public String outputFile(TableInfo tableInfo) {
-                    // 指定Mapper XML文件路径
-                    return finalMapperXmlOutputDir + finalModulePath + File.separator
-                            + tableInfo.getEntityName() + GeneratorConstant.MAPPER + StringPool.DOT_XML;
-                }
-            });
-        }
-
-        // 自定义pageParam模板
-        if (generatorConfig.isGeneratorPageParam()) {
-            focList.add(new FileOutConfig(GeneratorConstant.PAGE_PARAM_TEMPLATE_PATH) {
-                @Override
-                public String outputFile(TableInfo tableInfo) {
-                    return finalJavaOutputDir + File.separator + parentPackagePath + finalModulePath + File.separator + GeneratorConstant.PARAM +
-                            File.separator + tableInfo.getEntityName() + GeneratorConstant.PAGE_PARAM + StringPool.DOT_JAVA;
-                }
-            });
-        }
-
-        // 自定义queryVo模板
-        if (generatorConfig.isGeneratorQueryVo()) {
-            focList.add(new FileOutConfig(GeneratorConstant.QUERY_VO_TEMPLATE_PATH) {
-                @Override
-                public String outputFile(TableInfo tableInfo) {
-                    return finalJavaOutputDir + File.separator + parentPackagePath + finalModulePath + File.separator + GeneratorConstant.VO +
-                            File.separator + tableInfo.getEntityName() + GeneratorConstant.QUERY_VO + StringPool.DOT_JAVA;
-                }
-            });
-        }
-
-        injectionConfig.setFileOutConfigList(focList);
-
-        // 设置自定义配置
-        autoGenerator.setCfg(injectionConfig);
-
-        // 模版生成配置，设置为空，表示不生成
-        TemplateConfig templateConfig = new TemplateConfig();
-        // xml使用自定义输出
-        templateConfig.setXml(null);
-        // 是否生成entity
-        if (!generatorConfig.isGeneratorEntity()) {
-            templateConfig.setEntity(null);
-        }
-        // 是否生成controller
-        if (!generatorConfig.isGeneratorController()) {
-            templateConfig.setController(null);
-        }
-        // 是否生成service
-        if (!generatorConfig.isGeneratorService()) {
-            templateConfig.setService(null);
-        }
-        // 是否生成serviceImpl
-        if (!generatorConfig.isGeneratorServiceImpl()) {
-            templateConfig.setServiceImpl(null);
-        }
-        // 是否生成mapper
-        if (!generatorConfig.isGeneratorMapper()) {
-            templateConfig.setMapper(null);
-        }
-
-        // 设置模板引擎
-        autoGenerator.setTemplate(templateConfig);
-
-        // 执行代码生成
-        autoGenerator.execute();
-    }
-
-    /**
-     * 下划线专程驼峰命名
-     * sys_user --> sysUser
-     *
-     * @param underline
-     * @return
-     */
-    public static String underlineToCamel(String underline) {
-        if (StringUtils.isNotBlank(underline)) {
-            return NamingStrategy.underlineToCamel(underline);
-        }
-        return null;
-    }
-
-    /**
-     * 下划线转换成帕斯卡命名
-     * sys_user --> SysUser
-     *
-     * @param underline
-     * @return
-     */
-    public static String underlineToPascal(String underline) {
-        if (StringUtils.isNotBlank(underline)) {
-            return NamingStrategy.capitalFirst(NamingStrategy.underlineToCamel(underline));
-        }
-        return null;
-    }
-
-    /**
-     * 下划线转换成冒号连接命名
-     * sys_user --> sys:user
-     *
-     * @param underline
-     * @return
-     */
-    public static String underlineToColon(String underline) {
-        if (StringUtils.isBlank(underline)) {
-            return null;
-        }
-        String string = underline.toLowerCase();
-        return string.replaceAll("_", ":");
-    }
-
-    /**
-     * 首字母小写
-     *
-     * @param name
-     * @return
-     */
-    public static String toCamel(String name) {
-        if (StringUtils.isBlank(name)) {
-            return null;
-        }
-        return name.substring(0, 1).toLowerCase() + name.substring(1);
-    }
-
-}
diff --git a/generator/src/main/java/io/geekidea/springbootplus/generator/SpringBootPlusGenerator.java b/generator/src/main/java/io/geekidea/springbootplus/generator/SpringBootPlusGenerator.java
deleted file mode 100644
index 85e6fad7..00000000
--- a/generator/src/main/java/io/geekidea/springbootplus/generator/SpringBootPlusGenerator.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator;
-
-import com.baomidou.mybatisplus.annotation.DbType;
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.generator.config.rules.DateType;
-import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
-import io.geekidea.springbootplus.generator.config.GeneratorStrategy;
-import io.geekidea.springbootplus.generator.constant.GeneratorConstant;
-import io.geekidea.springbootplus.generator.properties.GeneratorProperties;
-import org.springframework.stereotype.Component;
-
-import java.util.Arrays;
-
-/**
- * spring-boot-plus代码生成器入口类
- *
- * @author geekidea
- * @date 2019-10-22
- **/
-@Component
-public class SpringBootPlusGenerator {
-
-    /**
-     * 生成代码
-     * @param args
-     */
-    public static void main(String[] args) {
-        GeneratorProperties generatorProperties = new GeneratorProperties();
-
-        // 设置基本信息
-        generatorProperties
-                .setMavenModuleName("example")
-                .setParentPackage("com.example")
-                .setModuleName("foobar")
-                .setAuthor("geekidea")
-                .setFileOverride(true);
-
-        // 设置表信息
-        generatorProperties.addTable("foo_bar","id");
-        // 设置表前缀
-        // generatorProperties.setTablePrefix(Arrays.asList("tb_"));
-
-        // 数据源配置
-        generatorProperties.getDataSourceConfig()
-                .setDbType(DbType.MYSQL)
-                .setUsername("root")
-                .setPassword("root")
-                .setDriverName("com.mysql.jdbc.Driver")
-                .setUrl("jdbc:mysql://localhost:3306/spring_boot_plus?useUnicode=true&characterEncoding=UTF-8&useSSL=false");
-
-        // 生成配置
-        generatorProperties.getGeneratorConfig()
-                .setGeneratorStrategy(GeneratorStrategy.SINGLE)
-                .setGeneratorEntity(true)
-                .setGeneratorController(true)
-                .setGeneratorService(true)
-                .setGeneratorServiceImpl(true)
-                .setGeneratorMapper(true)
-                .setGeneratorMapperXml(true)
-                .setGeneratorPageParam(true)
-                .setGeneratorQueryVo(true)
-                .setRequiresPermissions(false)
-                .setPageListOrder(true)
-                .setParamValidation(true)
-                .setSwaggerTags(true)
-                .setOperationLog(true);
-
-        // 全局配置
-        generatorProperties.getMybatisPlusGeneratorConfig().getGlobalConfig()
-                .setOpen(true)
-                .setSwagger2(true)
-                .setIdType(IdType.AUTO)
-                .setDateType(DateType.ONLY_DATE);
-
-        // 策略配置
-        generatorProperties.getMybatisPlusGeneratorConfig().getStrategyConfig()
-                .setNaming(NamingStrategy.underline_to_camel)
-                .setColumnNaming(NamingStrategy.underline_to_camel)
-                .setEntityLombokModel(true)
-                .setRestControllerStyle(true)
-                .setControllerMappingHyphenStyle(true)
-                .setVersionFieldName(GeneratorConstant.VERSION)
-                .setLogicDeleteFieldName(GeneratorConstant.DELETED);
-
-        // 生成代码
-        CodeGenerator codeGenerator = new CodeGenerator();
-        codeGenerator.generator(generatorProperties);
-
-    }
-
-
-}
diff --git a/generator/src/main/java/io/geekidea/springbootplus/generator/config/GeneratorConfig.java b/generator/src/main/java/io/geekidea/springbootplus/generator/config/GeneratorConfig.java
deleted file mode 100644
index e5c5cce1..00000000
--- a/generator/src/main/java/io/geekidea/springbootplus/generator/config/GeneratorConfig.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator.config;
-
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * 生成配置
- *
- * @author geekidea
- * @date 2020/3/13
- **/
-@Data
-@Accessors(chain = true)
-public class GeneratorConfig {
-    /**
-     * 代码生成策略
-     */
-    @NestedConfigurationProperty
-    public GeneratorStrategy generatorStrategy = GeneratorStrategy.ALL;
-    /**
-     * 是否生成实体类
-     */
-    private boolean generatorEntity = true;
-    /**
-     * 是否生成控制器
-     */
-    private boolean generatorController = true;
-    /**
-     * 是否生成service接口
-     */
-    private boolean generatorService = true;
-    /**
-     * 是否生成service实现类
-     */
-    private boolean generatorServiceImpl = true;
-    /**
-     * 是否生成Mapper
-     */
-    private boolean generatorMapper = true;
-    /**
-     * 是否生成Mapper XML
-     */
-    private boolean generatorMapperXml = true;
-    /**
-     * 是否生成查询参数
-     */
-    private boolean generatorPageParam = true;
-    /**
-     * 是否生成查询VO
-     */
-    private boolean generatorQueryVo = true;
-    /**
-     * 是否生成Shiro RequiresPermissions 注解
-     */
-    private boolean requiresPermissions = true;
-    /**
-     * 分页列表查询是否排序 true：有排序参数/false：无
-     */
-    private boolean pageListOrder = true;
-    /**
-     * 是否生成validation校验，true：生成/false：不生成
-     */
-    private boolean paramValidation = true;
-    /**
-     * 是否生成Swagger tags
-     * true: @Api(value = "系统用户API",tags = {"系统用户"})
-     * false: @Api("系统用户API")
-     */
-    private boolean swaggerTags = true;
-
-    /**
-     * 是否生成系统操作日志注解：@OperationLog
-     */
-    private boolean operationLog = true;
-
-}
diff --git a/generator/src/main/java/io/geekidea/springbootplus/generator/config/GeneratorStrategy.java b/generator/src/main/java/io/geekidea/springbootplus/generator/config/GeneratorStrategy.java
deleted file mode 100644
index c03621a0..00000000
--- a/generator/src/main/java/io/geekidea/springbootplus/generator/config/GeneratorStrategy.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator.config;
-
-/**
- * 生成策略
- * SIMPLE 生成最基本的代码
- * SINGLE 生成单表操作代码
- * ALL 生成所有的代码
- *
- * @author geekidea
- * @date 2020/3/13
- **/
-public enum GeneratorStrategy {
-    SIMPLE, SINGLE, ALL
-}
diff --git a/generator/src/main/java/io/geekidea/springbootplus/generator/config/MybatisPlusGeneratorConfig.java b/generator/src/main/java/io/geekidea/springbootplus/generator/config/MybatisPlusGeneratorConfig.java
deleted file mode 100644
index 1f93d853..00000000
--- a/generator/src/main/java/io/geekidea/springbootplus/generator/config/MybatisPlusGeneratorConfig.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator.config;
-
-import com.baomidou.mybatisplus.generator.config.GlobalConfig;
-import com.baomidou.mybatisplus.generator.config.PackageConfig;
-import com.baomidou.mybatisplus.generator.config.StrategyConfig;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-
-/**
- * mybatisplus相关配置
- * @author geekidea
- * @date 2020/3/13
- **/
-@Data
-@Accessors(chain = true)
-public class MybatisPlusGeneratorConfig {
-    /**
-     * 全局配置
-     */
-    @NestedConfigurationProperty
-    private GlobalConfig globalConfig = new GlobalConfig();
-
-    /**
-     * 包配置
-     */
-    @NestedConfigurationProperty
-    private PackageConfig packageConfig = new PackageConfig();
-
-    /**
-     * 策略配置
-     */
-    private StrategyConfig strategyConfig = new StrategyConfig();
-
-}
diff --git a/generator/src/main/java/io/geekidea/springbootplus/generator/config/ProjectConfig.java b/generator/src/main/java/io/geekidea/springbootplus/generator/config/ProjectConfig.java
deleted file mode 100644
index 463cc015..00000000
--- a/generator/src/main/java/io/geekidea/springbootplus/generator/config/ProjectConfig.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator.config;
-
-import io.geekidea.springbootplus.generator.constant.GeneratorConstant;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-/**
- * 项目信息配置
- * @author geekidea
- * @date 2020/3/13
- **/
-@Data
-@Accessors(chain = true)
-public class ProjectConfig {
-
-    /**
-     * 分页参数后缀
-     */
-    private String pageParamSuffix = GeneratorConstant.PAGE_PARAM;
-    /**
-     * QueryVo后缀
-     */
-    private String queryVoSuffix = GeneratorConstant.QUERY_VO;
-    /**
-     * 分页参数父类全称，带包名
-     */
-    private String superPageParamClass = GeneratorConstant.SUPER_PAGE_PARAM_CLASS;
-    /**
-     * 分页对象类全称，带包名
-     */
-    private String pagingClass = GeneratorConstant.PAGING_CLASS;
-    /**
-     * 分页信息类全称，带包名
-     */
-    private String pageInfoClass = GeneratorConstant.PAGE_INFO_CLASS;
-    /**
-     * 分页排序参数父类全称，带包名
-     */
-    private String superPageOrderParamClass = GeneratorConstant.SUPER_PAGE_ORDER_PARAM_CLASS;
-    /**
-     * 公共id参数类全称，带包名
-     */
-    private String idParamClass = GeneratorConstant.ID_PARAM_CLASS;
-    /**
-     * 公共结果类全称，带包名
-     */
-    private String apiResultClass = GeneratorConstant.API_RESULT_CLASS;
-}
diff --git a/generator/src/main/java/io/geekidea/springbootplus/generator/config/TableConfig.java b/generator/src/main/java/io/geekidea/springbootplus/generator/config/TableConfig.java
deleted file mode 100644
index 89fe4303..00000000
--- a/generator/src/main/java/io/geekidea/springbootplus/generator/config/TableConfig.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator.config;
-
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-/**
- * 生成的表信息配置
- * @author geekidea
- * @date 2020/3/13
- **/
-@Data
-@Accessors(chain = true)
-public class TableConfig {
-
-    /**
-     * 生成的表名称
-     */
-    private String tableName;
-
-    /**
-     * 主键数据库列名称
-     */
-    private String pkIdName = "id";
-
-}
diff --git a/generator/src/main/java/io/geekidea/springbootplus/generator/config/query/SpringBootPlusMySqlQuery.java b/generator/src/main/java/io/geekidea/springbootplus/generator/config/query/SpringBootPlusMySqlQuery.java
deleted file mode 100644
index e677bc8b..00000000
--- a/generator/src/main/java/io/geekidea/springbootplus/generator/config/query/SpringBootPlusMySqlQuery.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator.config.query;
-
-import com.baomidou.mybatisplus.generator.config.querys.MySqlQuery;
-
-/**
- * MySQL代码生成查询是否为空的列
- *
- * @author geekidea
- * @date 2019-10-11
- **/
-public class SpringBootPlusMySqlQuery extends MySqlQuery {
-
-    @Override
-    public String[] fieldCustom() {
-        return new String[]{"null", "default"};
-    }
-
-}
diff --git a/generator/src/main/java/io/geekidea/springbootplus/generator/config/query/SpringBootPlusSqlServerQuery.java b/generator/src/main/java/io/geekidea/springbootplus/generator/config/query/SpringBootPlusSqlServerQuery.java
deleted file mode 100644
index 862dabf7..00000000
--- a/generator/src/main/java/io/geekidea/springbootplus/generator/config/query/SpringBootPlusSqlServerQuery.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator.config.query;
-
-import com.baomidou.mybatisplus.generator.config.querys.SqlServerQuery;
-
-/**
- * SQLServer自定义生成器查询SQL
- *
- * @author geekidea
- * @date 2020-3-8
- **/
-public class SpringBootPlusSqlServerQuery extends SqlServerQuery {
-
-    /**
-     * fix mybatisplus3.3.1 SQLServer SQL过滤表，TABLE_NAME报错问题
-     * @return
-     */
-    @Override
-    public String tablesSql() {
-        return  "select TABLE_NAME,COMMENTS from (" +
-                "   select cast(so.name as varchar(500)) as TABLE_NAME, " +
-                "   cast(sep.value as varchar(500)) as COMMENTS from sysobjects so " +
-                "   left JOIN sys.extended_properties sep on sep.major_id=so.id and sep.minor_id=0 " +
-                "   where (xtype='U' or xtype='v')" +
-                ") tb " +
-                "where 1 = 1 ";
-
-    }
-}
diff --git a/generator/src/main/java/io/geekidea/springbootplus/generator/constant/GeneratorConstant.java b/generator/src/main/java/io/geekidea/springbootplus/generator/constant/GeneratorConstant.java
deleted file mode 100644
index e2f85e90..00000000
--- a/generator/src/main/java/io/geekidea/springbootplus/generator/constant/GeneratorConstant.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator.constant;
-
-import java.io.File;
-
-/**
- * 生成器常量
- *
- * @author geekidea
- * @date 2020/3/12
- **/
-public interface GeneratorConstant {
-
-    /**
-     * 用户目录
-     */
-    String USER_DIR = "user.dir";
-    /**
-     * java目录 src/main/java
-     */
-    String JAVA_DIR = File.separator + "src" + File.separator + "main" + File.separator + "java";
-    /**
-     * resources目录 src/main/resources
-     */
-    String RESOURCES_DIR = File.separator + "src" + File.separator + "main" + File.separator + "resources";
-    /**
-     * mapper目录 src/main/resources/mapper
-     */
-    String MAPPER_DIR = RESOURCES_DIR + File.separator + "mapper";
-    /**
-     * 参数
-     */
-    String PARAM = "param";
-    /**
-     * 分页参数
-     */
-    String PAGE_PARAM = "PageParam";
-    /**
-     * VO
-     */
-    String VO = "vo";
-    /**
-     * 查询VO
-     */
-    String QUERY_VO = "QueryVo";
-    /**
-     * Service
-     */
-    String SERVICE = "Service";
-    /**
-     * Mapper
-     */
-    String MAPPER = "Mapper";
-    /**
-     * Mapper XML template路径
-     */
-    String MAPPER_XML_TEMPLATE_PATH = File.separator + "templates" + File.separator + "mapper.xml.vm";
-    /**
-     * 分页参数 template路径
-     */
-    String PAGE_PARAM_TEMPLATE_PATH = File.separator + "templates" + File.separator + "pageParam.java.vm";
-    /**
-     * 查询VO template路径
-     */
-    String QUERY_VO_TEMPLATE_PATH = File.separator + "templates" + File.separator + "queryVo.java.vm";
-    /**
-     * 乐观锁属性名称
-     */
-    String VERSION = "version";
-    /**
-     * 逻辑删除属性名称
-     */
-    String DELETED = "deleted";
-    /**
-     * Service名称
-     */
-    String SERVICE_NAME = "%sService";
-    /**
-     * 自定义继承的Entity类全称，带包名
-     */
-    String SUPER_ENTITY_CLASS = "io.geekidea.springbootplus.framework.common.entity.BaseEntity";
-    /**
-     * 自定义继承的Controller类全称，带包名
-     */
-    String SUPER_CONTROLLER_CLASS = "io.geekidea.springbootplus.framework.common.controller.BaseController";
-    /**
-     * 自定义继承的Service类全称，带包名
-     */
-    String SUPER_SERVICE_CLASS = "io.geekidea.springbootplus.framework.common.service.BaseService";
-    /**
-     * 自定义继承的ServiceImpl类全称，带包名
-     */
-    String SUPER_SERVICE_IMPL_CLASS = "io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl";
-    /**
-     * 分页参数父类全称，带包名
-     */
-    String SUPER_PAGE_PARAM_CLASS = "io.geekidea.springbootplus.framework.core.pagination.BasePageParam";
-    /**
-     * 分页排序参数父类全称，带包名
-     */
-    String SUPER_PAGE_ORDER_PARAM_CLASS = "io.geekidea.springbootplus.framework.core.pagination.BasePageOrderParam";
-    /**
-     * 公共id参数类全称，带包名
-     */
-    String ID_PARAM_CLASS = "io.geekidea.springbootplus.framework.common.param.IdParam";
-    /**
-     * 分页对象类全称，带包名
-     */
-    String PAGING_CLASS = "io.geekidea.springbootplus.framework.core.pagination.Paging";
-    /**
-     * 分页信息类全称，带包名
-     */
-    String PAGE_INFO_CLASS = "io.geekidea.springbootplus.framework.core.pagination.PageInfo";
-    /**
-     * 公共结果类全称，带包名
-     */
-    String API_RESULT_CLASS = "io.geekidea.springbootplus.framework.common.api.ApiResult";
-    /**
-     * 分组验证Add.class类路径
-     */
-    String VALIDATOR_ADD_PACKAGE = "io.geekidea.springbootplus.framework.core.validator.groups.Add";
-    /**
-     * 分组验证Update.class类路径
-     */
-    String VALIDATOR_UPDATE_PACKAGE = "io.geekidea.springbootplus.framework.core.validator.groups.Update";
-}
diff --git a/generator/src/main/java/io/geekidea/springbootplus/generator/exception/GeneratorException.java b/generator/src/main/java/io/geekidea/springbootplus/generator/exception/GeneratorException.java
deleted file mode 100644
index 2421b141..00000000
--- a/generator/src/main/java/io/geekidea/springbootplus/generator/exception/GeneratorException.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator.exception;
-
-import io.geekidea.springbootplus.framework.common.api.ApiCode;
-import io.geekidea.springbootplus.framework.common.exception.SpringBootPlusException;
-
-/**
- * 代码生成异常
- *
- * @author geekidea
- * @date 2020/3/12
- */
-public class GeneratorException extends SpringBootPlusException {
-	private static final long serialVersionUID = 2556853577480934761L;
-
-	public GeneratorException(String message) {
-        super(message);
-    }
-
-    public GeneratorException(Integer errorCode, String message) {
-        super(errorCode, message);
-    }
-
-    public GeneratorException(ApiCode apiCode) {
-        super(apiCode);
-    }
-}
diff --git a/generator/src/main/java/io/geekidea/springbootplus/generator/properties/GeneratorProperties.java b/generator/src/main/java/io/geekidea/springbootplus/generator/properties/GeneratorProperties.java
deleted file mode 100644
index 07d4506f..00000000
--- a/generator/src/main/java/io/geekidea/springbootplus/generator/properties/GeneratorProperties.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator.properties;
-
-import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
-import io.geekidea.springbootplus.generator.config.GeneratorConfig;
-import io.geekidea.springbootplus.generator.config.MybatisPlusGeneratorConfig;
-import io.geekidea.springbootplus.generator.config.ProjectConfig;
-import io.geekidea.springbootplus.generator.config.TableConfig;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.context.properties.NestedConfigurationProperty;
-import org.springframework.stereotype.Component;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * spring-boot-plus代码生成配置属性
- *
- * @author geekidea
- * @date 2020/3/11
- **/
-@Data
-@Accessors(chain = true)
-@Component
-@ConfigurationProperties(prefix = "spring-boot-plus.generator")
-public class GeneratorProperties {
-
-    /**
-     * Maven模块名称
-     */
-    private String mavenModuleName;
-
-    /**
-     * 业务模块名称
-     */
-    private String moduleName;
-
-    /**
-     * 生成的父包全路径名称
-     */
-    private String parentPackage;
-
-    /**
-     * 生成的类文件路径
-     */
-    private String parentPackagePath;
-
-    /**
-     * 开发人员名称
-     */
-    private String author;
-
-    /**
-     * 生成文件的输出目录,root: 表示当前项目根目录
-     */
-    private String outputDir;
-
-    /**
-     * 是否覆盖已有文件
-     */
-    private boolean fileOverride = false;
-
-    /**
-     * 表前缀
-     */
-    private List<String> tablePrefix;
-
-    /**
-     * 表信息配置
-     */
-    @NestedConfigurationProperty
-    private List<TableConfig> tableConfig = new ArrayList<>();
-
-    /**
-     * 数据库连接信息配置
-     */
-    @NestedConfigurationProperty
-    public DataSourceConfig dataSourceConfig = new DataSourceConfig();
-
-    /**
-     * 代码生成配置
-     */
-    @NestedConfigurationProperty
-    private GeneratorConfig generatorConfig = new GeneratorConfig();
-
-    /**
-     * mybatisplus相关配置
-     */
-    @NestedConfigurationProperty
-    private MybatisPlusGeneratorConfig mybatisPlusGeneratorConfig = new MybatisPlusGeneratorConfig();
-
-    /**
-     * 项目信息配置
-     */
-    @NestedConfigurationProperty
-    private ProjectConfig projectConfig = new ProjectConfig();
-
-
-    public GeneratorProperties addTable(String tableName) {
-        this.addTable(tableName, null);
-        return this;
-    }
-
-    public GeneratorProperties addTable(String tableName, String pkIdName) {
-        this.getTableConfig().add(new TableConfig().setTableName(tableName).setPkIdName(pkIdName));
-        return this;
-    }
-
-    public GeneratorProperties setTables(String... tableNames) {
-        if (tableNames == null) {
-            return this;
-        }
-        for (String tableName : tableNames) {
-            this.addTable(tableName);
-        }
-        return this;
-    }
-}
diff --git a/generator/src/main/resources/application.yml b/generator/src/main/resources/application.yml
deleted file mode 100644
index 65ba71c1..00000000
--- a/generator/src/main/resources/application.yml
+++ /dev/null
@@ -1,90 +0,0 @@
-spring-boot-plus:
-  generator:
-    # Maven模块名称
-    maven-module-name: example
-    # 生成的父包全路径名称
-    parent-package: com.example
-    # 业务模块名称
-    module-name: foorbar
-    # 表前缀
-    table-prefix:
-    # 生成的表名称，可配置多张表
-    table-config:
-      # 表名称
-      - table-name: foo_bar
-        # 主键列名称
-        pk-id-name: id
-    # 开发人员名称
-    author: geekidea
-    # 是否覆盖已有文件
-    file-override: true
-    # 生成文件的输出目录,如果为空: 表示当前项目根目录
-    output-dir:
-    # 数据源配置
-    data-source-config:
-      # 数据库类型
-      db-type: MYSQL
-      # 数据库连接用户名
-      username: root
-      # 数据库连接密码
-      password: root
-      # 驱动URL
-      driver-name: com.mysql.jdbc.Driver
-      # 驱动URL
-      url: jdbc:mysql://localhost:3306/spring_boot_plus?useUnicode=true&characterEncoding=UTF-8&useSSL=false
-    # 代码生成配置
-    generator-config:
-      # 代码生成策略 SIMPLE:生成最基本的代码，NORMAL:生成普通代码， ALL:生成所有的代码
-      generator-strategy: ALL
-      # 是否生成实体类
-      generator-entity: true
-      # 是否生成控制器
-      generator-controller: true
-      # 是否生成service接口
-      generator-service: true
-      # 是否生成service实现类
-      generator-service-impl: true
-      # 是否生成Mapper
-      generator-mapper: true
-      # 是否生成Mapper XML
-      generator-mapper-xml: true
-      # 是否生成查询参数
-      generator-page-param: true
-      # 是否生成查询VO
-      generator-query-vo: true
-      # 是否生成Shiro RequiresPermissions 注解
-      requires-permissions: true
-      # 分页列表查询是否排序 true：有排序参数/false：无
-      page-list-order: true
-      # 是否生成validation校验，true：生成/false：不生成
-      param-validation: true
-    # mybatisplus配置
-    mybatis-plus-generator-config:
-      global-config:
-        # 是否打开输出目录
-        open: false
-        # 开启 swagger2 模式
-        swagger2: true
-        # 指定生成的主键的ID类型
-        id-type: AUTO
-        # 时间类型对应策略
-        date-type: ONLY_DATE
-      strategy-config:
-        # 数据库表映射到实体的命名策略
-        naming: underline_to_camel
-        # 数据库表字段映射到实体的命名策略
-        column-naming: underline_to_camel
-        # 实体】是否为lombok模型
-        entity-lombok-model: true
-        # RestController
-        rest-controller-style: true
-        # 自定义基础的Entity类，公共字段
-        super-entity-columns:
-        # 表填充字段
-        table-fill-list:
-        # 驼峰转连字符
-        controller-mapping-hyphenstyle: true
-        # 乐观锁属性名称
-        version-field-name: version
-        # 逻辑删除属性名称
-        logic-delete-field-name: deleted
\ No newline at end of file
diff --git a/generator/src/main/resources/templates/controller.java.vm b/generator/src/main/resources/templates/controller.java.vm
deleted file mode 100644
index 15533129..00000000
--- a/generator/src/main/resources/templates/controller.java.vm
+++ /dev/null
@@ -1,162 +0,0 @@
-package ${package.Controller};
-
-import ${package.Entity}.${entity};
-import ${package.Service}.${table.serviceName};
-import lombok.extern.slf4j.Slf4j;
-#if(${cfg.generatorStrategy} != 'SIMPLE')
-#if(${cfg.generatorPageParam})
-import ${cfg.pageParamClass};
-#end
-#if(${superControllerClassPackage})
-import ${superControllerClassPackage};
-#end
-#if(${cfg.generatorQueryVo})
-import ${cfg.queryVoClass};
-#end
-import ${cfg.apiResultClass};
-import ${cfg.pagingClass};
-import ${cfg.idParamClass};
-#if(${cfg.operationLog})
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-#end
-#if(${cfg.paramValidation})
-import ${cfg.validatorAddPackage};
-import ${cfg.validatorUpdatePackage};
-import org.springframework.validation.annotation.Validated;
-#end
-#if(${cfg.requiresPermissions})
-import org.apache.shiro.authz.annotation.RequiresPermissions;
-#end
-#end
-#if(${swagger2})
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-#end
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-/**
- * $!{table.comment} 控制器
- *
- * @author ${author}
- * @since ${date}
- */
-@Slf4j
-#if(${restControllerStyle})
-@RestController
-#else
-@Controller
-#end
-@RequestMapping("/${cfg.entityObjectName}")
-#if(${cfg.operationLog})
-@Module("${cfg.module}")
-#end
-#if(${cfg.swaggerTags})
-@Api(value = "$!{table.comment}API", tags = {"$!{table.comment}"})
-#else
-@Api("$!{table.comment}API")
-#end
-#if(${kotlin})
-class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end
-
-#else
-#if(${superControllerClass})
-public class ${table.controllerName} extends ${superControllerClass} {
-#else
-public class ${table.controllerName} {
-#end
-
-    @Autowired
-    private ${table.serviceName} ${cfg.serviceObjectName};
-
-#if(${cfg.generatorStrategy} != 'SIMPLE')
-    /**
-     * 添加$!{table.comment}
-     */
-    @PostMapping("/add")
-#if(${cfg.requiresPermissions})
-    @RequiresPermissions("$!{cfg.colonTableName}:add")
-#end
-#if(${cfg.operationLog})
-    @OperationLog(name = "添加$!{table.comment}", type = OperationLogType.ADD)
-#end
-    @ApiOperation(value = "添加$!{table.comment}", response = ApiResult.class)
-    public ApiResult<Boolean> add${entity}(#if(${cfg.paramValidation})@Validated(Add.class) #end@RequestBody ${entity} ${cfg.entityObjectName}) throws Exception {
-        boolean flag = ${cfg.serviceObjectName}.save${entity}(${cfg.entityObjectName});
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 修改$!{table.comment}
-     */
-    @PostMapping("/update")
-#if(${cfg.requiresPermissions})
-    @RequiresPermissions("$!{cfg.colonTableName}:update")
-#end
-#if(${cfg.operationLog})
-    @OperationLog(name = "修改$!{table.comment}", type = OperationLogType.UPDATE)
-#end
-    @ApiOperation(value = "修改$!{table.comment}", response = ApiResult.class)
-    public ApiResult<Boolean> update${entity}(#if(${cfg.paramValidation})@Validated(Update.class) #end@RequestBody ${entity} ${cfg.entityObjectName}) throws Exception {
-        boolean flag = ${cfg.serviceObjectName}.update${entity}(${cfg.entityObjectName});
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 删除$!{table.comment}
-     */
-    @PostMapping("/delete/{id}")
-#if(${cfg.requiresPermissions})
-    @RequiresPermissions("$!{cfg.colonTableName}:delete")
-#end
-#if(${cfg.operationLog})
-    @OperationLog(name = "删除$!{table.comment}", type = OperationLogType.DELETE)
-#end
-    @ApiOperation(value = "删除$!{table.comment}", response = ApiResult.class)
-    public ApiResult<Boolean> delete${entity}(@PathVariable("id") Long id) throws Exception {
-        boolean flag = ${cfg.serviceObjectName}.delete${entity}(id);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 获取$!{table.comment}详情
-     */
-    @GetMapping("/info/{id}")
-#if(${cfg.requiresPermissions})
-    @RequiresPermissions("$!{cfg.colonTableName}:info")
-#end
-#if(${cfg.operationLog})
-    @OperationLog(name = "$!{table.comment}详情", type = OperationLogType.INFO)
-#end
-    @ApiOperation(value = "$!{table.comment}详情", response = ${entity}${cfg.queryVo}.class)
-    public ApiResult<${entity}${cfg.queryVo}> get${entity}(@PathVariable("id") Long id) throws Exception {
-#if(${cfg.generatorStrategy} == 'SINGLE')
-        ${entity}${cfg.queryVo} ${cfg.entityObjectName}${cfg.queryVo} = ${cfg.serviceObjectName}.getById(id);
-#else
-        ${entity}${cfg.queryVo} ${cfg.entityObjectName}${cfg.queryVo} = ${cfg.serviceObjectName}.get${entity}ById(id);
-#end
-        return ApiResult.ok(${cfg.entityObjectName}${cfg.queryVo});
-    }
-
-    /**
-     * $!{table.comment}分页列表
-     */
-    @PostMapping("/getPageList")
-#if(${cfg.requiresPermissions})
-    @RequiresPermissions("$!{cfg.colonTableName}:page")
-#end
-#if(${cfg.operationLog})
-    @OperationLog(name = "$!{table.comment}分页列表", type = OperationLogType.PAGE)
-#end
-    @ApiOperation(value = "$!{table.comment}分页列表", response = ${entity}${cfg.queryVo}.class)
-    public ApiResult<Paging<${entity}${cfg.queryVo}>> get${entity}PageList(#if(${cfg.paramValidation})@Validated #end@RequestBody ${entity}${cfg.pageParam} ${cfg.entityObjectName}${cfg.pageParam}) throws Exception {
-        Paging<${entity}${cfg.queryVo}> paging = ${cfg.entityObjectName}Service.get${entity}PageList(${cfg.entityObjectName}${cfg.pageParam});
-        return ApiResult.ok(paging);
-    }
-#end
-
-}
-
-#end
\ No newline at end of file
diff --git a/generator/src/main/resources/templates/entity.java.vm b/generator/src/main/resources/templates/entity.java.vm
deleted file mode 100644
index 3f0e37d6..00000000
--- a/generator/src/main/resources/templates/entity.java.vm
+++ /dev/null
@@ -1,104 +0,0 @@
-package ${package.Entity};
-
-#foreach($pkg in ${table.importPackages})
-import ${pkg};
-#end
-#if(${swagger2})
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-#end
-#if(${entityLombokModel})
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-#end
-#if(${cfg.paramValidation})
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import ${cfg.validatorUpdatePackage};
-#end
-
-/**
- * $!{table.comment}
- *
- * @author ${author}
- * @since ${date}
- */
-#if(${entityLombokModel})
-@Data
-@Accessors(chain = true)
-#if(${superEntityClass})
-@EqualsAndHashCode(callSuper = true)
-#else
-@EqualsAndHashCode(callSuper = false)
-#end
-#end
-#if(${table.convert})
-@TableName("${table.name}")
-#end
-#if(${swagger2})
-@ApiModel(value = "${entity}对象")
-#end
-#if(${superEntityClass})
-public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {
-#elseif(${activeRecord})
-public class ${entity} extends Model<${entity}> {
-#else
-public class ${entity} implements Serializable {
-#end
-    private static final long serialVersionUID = 1L;
-
-## ----------  BEGIN 字段循环遍历  ----------
-#foreach($field in ${table.fields})
-## 如果没有默认值，且不为空，则设置非空校验
-#if(${cfg.paramValidation} && ${field.customMap.null} == 'NO' && !${field.customMap.default})
-    #if(${field.keyIdentityFlag})
-    @NotNull(message = "${field.propertyName}不能为空", groups = {Update.class})
-    #elseif(${field.propertyType} == 'String')
-    @NotBlank(message = "${field.comment}不能为空")
-    #else
-    @NotNull(message = "${field.comment}不能为空")
-    #end
-#end
-#if("$!field.comment" != "")
-    #if(${swagger2})
-    @ApiModelProperty("${field.comment}")
-    #else
-/**
-     * ${field.comment}
-     */
-     #end
-#end
-#if(${field.keyFlag})
-## 主键
-#if(${field.keyIdentityFlag})
-    @TableId(value = "${field.name}", type = IdType.AUTO)
-#elseif(!$null.isNull(${idType}) && "$!idType" != "")
-## 设置主键注解
-    @TableId(value = "${field.name}", type = IdType.${idType})
-#elseif(${field.convert})
-    @TableId("${field.name}")
-#end
-## 普通字段
-#elseif(${field.fill})
-## -----   存在字段填充设置   -----
-#if(${field.convert})
-    @TableField(value = "${field.name}", fill = FieldFill.${field.fill})
-#else
-    @TableField(fill = FieldFill.${field.fill})
-#end
-#elseif(${field.convert})
-    @TableField("${field.name}")
-#end
-## 乐观锁注解
-#if(${versionFieldName}==${field.name})
-    @Version
-#end
-## 逻辑删除注解
-#if(${logicDeleteFieldName}==${field.name})
-    @TableLogic
-#end
-    private ${field.propertyType} ${field.propertyName};
-
-#end
-}
diff --git a/generator/src/main/resources/templates/mapper.java.vm b/generator/src/main/resources/templates/mapper.java.vm
deleted file mode 100644
index 3131cbef..00000000
--- a/generator/src/main/resources/templates/mapper.java.vm
+++ /dev/null
@@ -1,53 +0,0 @@
-package ${package.Mapper};
-
-import ${superMapperClassPackage};
-import ${package.Entity}.${entity};
-#if(${cfg.generatorPageParam})
-import ${cfg.pageParamClass};
-#end
-#if(${cfg.generatorQueryVo})
-import ${cfg.queryVoClass};
-#end
-
-import org.springframework.stereotype.Repository;
-
-#if(${cfg.generatorStrategy} != 'SIMPLE')
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import org.apache.ibatis.annotations.Param;
-import java.io.Serializable;
-#end
-
-/**
- * $!{table.comment} Mapper 接口
- *
- * @author ${author}
- * @since ${date}
- */
-#if(${kotlin})
-interface ${table.mapperName} : ${superMapperClass}<${entity}>
-#else
-@Repository
-public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {
-
-#if(${cfg.generatorStrategy} == 'ALL')
-    /**
-     * 根据ID获取查询对象
-     *
-     * @param id
-     * @return
-     */
-    ${entity}${cfg.queryVo} get${entity}ById(Serializable id);
-
-    /**
-     * 获取分页对象
-     *
-     * @param page
-     * @param ${cfg.entityObjectName}QueryParam
-     * @return
-     */
-    IPage<${entity}${cfg.queryVo}> get${entity}PageList(@Param("page") Page page, @Param("param") ${entity}${cfg.pageParam} ${cfg.entityObjectName}${cfg.pageParam});
-#end
-
-}
-#end
diff --git a/generator/src/main/resources/templates/mapper.xml.vm b/generator/src/main/resources/templates/mapper.xml.vm
deleted file mode 100644
index b46f3ed0..00000000
--- a/generator/src/main/resources/templates/mapper.xml.vm
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="${package.Mapper}.${table.mapperName}">
-
-#if(${enableCache})
-    <!-- 开启二级缓存 -->
-    <cache type="org.mybatis.caches.ehcache.LoggingEhcache"/>
-
-#end
-#if(${baseResultMap})
-    <!-- 通用查询映射结果 -->
-    <resultMap id="BaseResultMap" type="${package.Entity}.${entity}">
-#foreach($field in ${table.fields})
-#if(${field.keyFlag})##生成主键排在第一位
-        <id column="${field.name}" property="${field.propertyName}" />
-#end
-#end
-#foreach($field in ${table.commonFields})##生成公共字段
-    <result column="${field.name}" property="${field.propertyName}" />
-#end
-#foreach($field in ${table.fields})
-#if(!${field.keyFlag})##生成普通字段
-        <result column="${field.name}" property="${field.propertyName}" />
-#end
-#end
-    </resultMap>
-
-#end
-#if(${cfg.generatorStrategy} == 'ALL')
-    <!-- 通用查询结果列 -->
-    <sql id="Base_Column_List">
-#foreach($field in ${table.commonFields})
-        ${field.name},
-#end
-        ${table.fieldNames}
-    </sql>
-
-    <select id="get${entity}ById" resultType="${cfg.queryVoClass}">
-        select
-        <include refid="Base_Column_List"/>
-        from ${table.name} where ${cfg.pkIdName} = #{id}
-    </select>
-
-    <select id="get${entity}PageList" parameterType="${cfg.pageParamClass}" resultType="${cfg.queryVoClass}">
-        select
-        <include refid="Base_Column_List"/>
-        from ${table.name}
-    </select>
-
-#end
-</mapper>
diff --git a/generator/src/main/resources/templates/pageParam.java.vm b/generator/src/main/resources/templates/pageParam.java.vm
deleted file mode 100644
index 75745291..00000000
--- a/generator/src/main/resources/templates/pageParam.java.vm
+++ /dev/null
@@ -1,31 +0,0 @@
-package ${cfg.pageParamPackage};
-
-import io.swagger.annotations.ApiModel;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-#if(${cfg.pageListOrder})
-import ${cfg.superPageOrderParamClass};
-#else
-import ${cfg.superPageParamClass};
-#end
-
-/**
- * <pre>
- * $!{table.comment} 分页参数对象
- * </pre>
- *
- * @author ${author}
- * @date ${date}
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "$!{table.comment}分页参数")
-#if(${cfg.pageListOrder})
-public class ${entity}${cfg.pageParam} extends BasePageOrderParam {
-#else
-public class ${entity}${cfg.pageParam} extends BasePageParam {
-#end
-    private static final long serialVersionUID = 1L;
-}
diff --git a/generator/src/main/resources/templates/queryVo.java.vm b/generator/src/main/resources/templates/queryVo.java.vm
deleted file mode 100644
index 027d7fa9..00000000
--- a/generator/src/main/resources/templates/queryVo.java.vm
+++ /dev/null
@@ -1,46 +0,0 @@
-package ${cfg.queryVoPackage};
-
-#if(${swagger2})
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-#end
-#if(${entityLombokModel})
-import lombok.Data;
-import lombok.experimental.Accessors;
-#end
-import java.io.Serializable;
-import java.util.Date;
-
-/**
- * <pre>
- * $!{table.comment} 查询结果对象
- * </pre>
- *
- * @author ${author}
- * @date ${date}
- */
-#if(${entityLombokModel})
-@Data
-@Accessors(chain = true)
-#end
-@ApiModel(value = "${entity}${cfg.queryVo}对象")
-public class ${entity}${cfg.queryVo} implements Serializable {
-    private static final long serialVersionUID = 1L;
-## ----------  BEGIN 字段循环遍历  ----------
-#foreach($field in ${table.fields})
-
-#if(${field.keyFlag})
-#set($keyPropertyName=${field.propertyName})
-#end
-#if("$!field.comment" != "")
-    #if(${swagger2})
-    @ApiModelProperty("${field.comment}")
-    #else
-/**
-     * ${field.comment}
-     */
-     #end
-#end
-    private ${field.propertyType} ${field.propertyName};
-#end
-}
\ No newline at end of file
diff --git a/generator/src/main/resources/templates/service.java.vm b/generator/src/main/resources/templates/service.java.vm
deleted file mode 100644
index e78827ff..00000000
--- a/generator/src/main/resources/templates/service.java.vm
+++ /dev/null
@@ -1,72 +0,0 @@
-package ${package.Service};
-
-import ${package.Entity}.${entity};
-#if(${cfg.generatorPageParam})
-import ${cfg.pageParamClass};
-#end
-import ${superServiceClassPackage};
-#if(${cfg.generatorQueryVo})
-import ${cfg.queryVoClass};
-#end
-#if(${cfg.generatorStrategy} != 'SIMPLE')
-import ${cfg.pagingClass};
-#end
-
-/**
- * $!{table.comment} 服务类
- *
- * @author ${author}
- * @since ${date}
- */
-public interface ${table.serviceName} extends ${superServiceClass}<${entity}> {
-#if(${cfg.generatorStrategy} != 'SIMPLE')
-
-    /**
-     * 保存
-     *
-     * @param ${cfg.entityObjectName}
-     * @return
-     * @throws Exception
-     */
-    boolean save${entity}(${entity} ${cfg.entityObjectName}) throws Exception;
-
-    /**
-     * 修改
-     *
-     * @param ${cfg.entityObjectName}
-     * @return
-     * @throws Exception
-     */
-    boolean update${entity}(${entity} ${cfg.entityObjectName}) throws Exception;
-
-    /**
-     * 删除
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    boolean delete${entity}(Long id) throws Exception;
-
-    #if(${cfg.generatorStrategy} != 'SINGLE')
-    /**
-     * 根据ID获取查询对象
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    ${entity}${cfg.queryVo} get${entity}ById(Serializable id) throws Exception;
-    #end
-
-    /**
-     * 获取分页对象
-     *
-     * @param ${cfg.entityObjectName}QueryParam
-     * @return
-     * @throws Exception
-     */
-    Paging<${entity}${cfg.queryVo}> get${entity}PageList(${entity}${cfg.pageParam} ${cfg.entityObjectName}${cfg.pageParam}) throws Exception;
-#end
-
-}
diff --git a/generator/src/main/resources/templates/serviceImpl.java.vm b/generator/src/main/resources/templates/serviceImpl.java.vm
deleted file mode 100644
index e77efe6b..00000000
--- a/generator/src/main/resources/templates/serviceImpl.java.vm
+++ /dev/null
@@ -1,80 +0,0 @@
-package ${package.ServiceImpl};
-
-import ${package.Entity}.${entity};
-import ${package.Mapper}.${table.mapperName};
-import ${package.Service}.${table.serviceName};
-#if(${cfg.generatorPageParam})
-import ${cfg.pageParamClass};
-#end
-#if(${cfg.generatorQueryVo})
-import ${cfg.queryVoClass};
-#end
-#if(${cfg.generatorStrategy} == 'SINGLE')
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-#end
-import ${superServiceImplClassPackage};
-#if(${cfg.generatorStrategy} != 'SIMPLE')
-import ${cfg.pagingClass};
-import ${cfg.pageInfoClass};
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import org.springframework.transaction.annotation.Transactional;
-#end
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.beans.factory.annotation.Autowired;
-
-/**
- * $!{table.comment} 服务实现类
- *
- * @author ${author}
- * @since ${date}
- */
-@Slf4j
-@Service
-public class ${table.serviceImplName} extends BaseServiceImpl<${table.mapperName}, ${entity}> implements ${table.serviceName} {
-
-    @Autowired
-    private ${table.mapperName} ${cfg.mapperObjectName};
-#if(${cfg.generatorStrategy} != 'SIMPLE')
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean save${entity}(${entity} ${cfg.entityObjectName}) throws Exception {
-        return super.save(${cfg.entityObjectName});
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean update${entity}(${entity} ${cfg.entityObjectName}) throws Exception {
-        return super.updateById(${cfg.entityObjectName});
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean delete${entity}(Long id) throws Exception {
-        return super.removeById(id);
-    }
-
-    #if(${cfg.generatorStrategy} != 'SINGLE')
-    @Override
-    public ${entity}${cfg.queryVo} get${entity}ById(Serializable id) throws Exception {
-    return ${cfg.mapperObjectName}.get${entity}ById(id);
-    }
-
-    #end
-    @Override
-    public Paging<${entity}${cfg.queryVo}> get${entity}PageList(${entity}${cfg.pageParam} ${cfg.entityObjectName}${cfg.pageParam}) throws Exception {
-        Page<${entity}${cfg.queryVo}> page = new PageInfo<>(${cfg.entityObjectName}${cfg.pageParam}, OrderItem.desc(getLambdaColumn(${entity}::getCreateTime)));
-    #if(${cfg.generatorStrategy} != 'SINGLE')
-        IPage<${entity}${cfg.queryVo}> iPage = ${cfg.mapperObjectName}.get${entity}PageList(page, ${cfg.entityObjectName}${cfg.pageParam});
-    #else
-        LambdaQueryWrapper<${entity}> wrapper = new LambdaQueryWrapper<>();
-        IPage<${entity}${cfg.queryVo}> iPage = ${cfg.mapperObjectName}.selectPage(page, wrapper);
-    #end
-        return new Paging<${entity}${cfg.queryVo}>(iPage);
-    }
-#end
-
-}
diff --git a/generator/src/test/java/io/geekidea/springbootplus/generator/test/GeneratorPropertiesTest.java b/generator/src/test/java/io/geekidea/springbootplus/generator/test/GeneratorPropertiesTest.java
deleted file mode 100644
index 3b6a1469..00000000
--- a/generator/src/test/java/io/geekidea/springbootplus/generator/test/GeneratorPropertiesTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator.test;
-
-import io.geekidea.springbootplus.generator.CodeGenerator;
-import io.geekidea.springbootplus.generator.properties.GeneratorProperties;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringRunner;
-
-/**
- * 生成代码配置文件读取测试
- *
- * @author geekidea
- * @date 2020/3/12
- **/
-@RunWith(SpringRunner.class)
-@SpringBootTest(classes = GeneratorTestApplication.class)
-public class GeneratorPropertiesTest {
-
-    @Autowired
-    private GeneratorProperties generatorProperties;
-
-    @Autowired
-    private CodeGenerator codeGenerator;
-
-    @Test
-    public void test(){
-        codeGenerator.generator(generatorProperties);
-    }
-
-}
diff --git a/generator/src/test/java/io/geekidea/springbootplus/generator/test/GeneratorTestApplication.java b/generator/src/test/java/io/geekidea/springbootplus/generator/test/GeneratorTestApplication.java
deleted file mode 100644
index f17489da..00000000
--- a/generator/src/test/java/io/geekidea/springbootplus/generator/test/GeneratorTestApplication.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.generator.test;
-
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-/**
- * @author geekidea
- * @date 2020/3/12
- **/
-@SpringBootApplication(scanBasePackages = "io.geekidea.springbootplus.generator")
-public class GeneratorTestApplication {
-}
diff --git a/pom.xml b/pom.xml
index a4f929f5..db1ea737 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,37 +1,26 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<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">
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
-
-    <groupId>io.geekidea.springbootplus</groupId>
-    <artifactId>parent</artifactId>
-    <version>2.0</version>
-    <packaging>pom</packaging>
-
-    <name>spring-boot-plus</name>
-    <description>spring-boot-plus Project</description>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.7.18</version>
+        <relativePath/>
+    </parent>
+
+	<groupId>io.geekidea.boot</groupId>
+	<artifactId>spring-boot-plus</artifactId>
+    <version>2.7.18</version>
+
+	<name>spring-boot-plus</name>
+    <description>spring-boot-plus</description>
     <url>https://github.com/geekidea/spring-boot-plus</url>
 
     <licenses>
         <license>
-            <name>The Apache License, Version 2.0</name>
-            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+            <name>MIT License</name>
+            <url>https://www.mit-license.org/</url>
         </license>
     </licenses>
 
@@ -40,302 +29,15 @@
             <id>spring-boot-plus</id>
             <name>geekidea</name>
             <email>geekidea@qq.com</email>
+            <url>https://springboot.plus</url>
+            <organizationUrl>https://geekidea.io</organizationUrl>
         </developer>
     </developers>
 
     <properties>
-        <utf8>UTF-8</utf8>
-        <boot.artifact.name>bootstrap-${project.version}</boot.artifact.name>
-        <assembly.name>spring-boot-plus-server-${project.version}</assembly.name>
-        <admin.artifact.name>admin-${project.version}</admin.artifact.name>
-        <admin.assembly.name>admin-${project.version}</admin.assembly.name>
-        <project.build.sourceEncoding>${utf8}</project.build.sourceEncoding>
-        <project.reporting.outputEncoding>${utf8}</project.reporting.outputEncoding>
         <java.version>1.8</java.version>
-        <spring-boot.version>2.2.5.RELEASE</spring-boot.version>
-        <spring-boot-admin.version>2.2.2</spring-boot-admin.version>
-        <mybatis-plus-boot-starter.version>3.3.1</mybatis-plus-boot-starter.version>
-        <velocity.version>2.2</velocity.version>
-        <commons-lang3.version>3.9</commons-lang3.version>
-        <commons-collections4.version>4.4</commons-collections4.version>
-        <commons-io.version>2.6</commons-io.version>
-        <commons-codec.version>1.14</commons-codec.version>
-        <commons-net.version>3.6</commons-net.version>
-        <commons-pool2.version>2.8.0</commons-pool2.version>
-        <commons-text.version>1.8</commons-text.version>
-        <fastjson.version>1.2.67</fastjson.version>
-        <mysql.version>5.1.47</mysql.version>
-        <reflections.version>0.9.9</reflections.version>
-        <jansi.version>1.18</jansi.version>
-        <lombok.version>1.18.12</lombok.version>
-        <hutool.version>5.2.4</hutool.version>
-        <junit.version>4.12</junit.version>
-        <ini4j.version>0.5.4</ini4j.version>
-        <mapstruct.version>1.3.1.Final</mapstruct.version>
-        <shiro.version>1.5.1</shiro.version>
-        <jwt.version>3.10.1</jwt.version>
-        <guava.version>28.2-jre</guava.version>
-        <snakeyaml.version>1.26</snakeyaml.version>
-        <swagger2.version>2.9.2</swagger2.version>
-        <knife4j.version>2.0.2</knife4j.version>
-
-        <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
-        <maven-resources-plugin.version>3.1.0</maven-resources-plugin.version>
-        <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
-        <maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
-        <maven-versions-plugin.version>2.2</maven-versions-plugin.version>
-        <maven-assembly-plugin.version>3.2.0</maven-assembly-plugin.version>
     </properties>
 
-    <modules>
-        <module>admin</module>
-        <module>bootstrap</module>
-        <module>config</module>
-        <module>distribution</module>
-        <module>example</module>
-        <module>framework</module>
-        <module>generator</module>
-        <module>scheduled</module>
-        <module>system</module>
-    </modules>
-
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-dependencies</artifactId>
-                <version>${spring-boot.version}</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-            <!-- spring-boot start -->
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-starter-web</artifactId>
-                <version>${spring-boot.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-starter-aop</artifactId>
-                <version>${spring-boot.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-starter-tomcat</artifactId>
-                <version>${spring-boot.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-starter-jdbc</artifactId>
-                <version>${spring-boot.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-starter-data-redis</artifactId>
-                <version>${spring-boot.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-starter-json</artifactId>
-                <version>${spring-boot.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-starter-validation</artifactId>
-                <version>${spring-boot.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-configuration-processor</artifactId>
-                <version>${spring-boot.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-starter-security</artifactId>
-                <version>${spring-boot.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-starter-test</artifactId>
-                <version>${spring-boot.version}</version>
-                <scope>test</scope>
-            </dependency>
-            <!-- spring-boot end -->
-
-            <!-- mybatis-plus begin -->
-            <dependency>
-                <groupId>com.baomidou</groupId>
-                <artifactId>mybatis-plus-boot-starter</artifactId>
-                <version>${mybatis-plus-boot-starter.version}</version>
-            </dependency>
-            <!-- mybatis-plus end -->
-
-            <!-- MySQL -->
-            <dependency>
-                <groupId>mysql</groupId>
-                <artifactId>mysql-connector-java</artifactId>
-                <version>${mysql.version}</version>
-            </dependency>
-
-            <!-- swagger start -->
-            <dependency>
-                <groupId>io.springfox</groupId>
-                <artifactId>springfox-swagger2</artifactId>
-                <version>${swagger2.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>io.springfox</groupId>
-                <artifactId>springfox-swagger-ui</artifactId>
-                <version>${swagger2.version}</version>
-            </dependency>
-            <!-- swagger end -->
-
-            <dependency>
-                <groupId>com.github.xiaoymin</groupId>
-                <artifactId>knife4j-spring-boot-starter</artifactId>
-                <version>${knife4j.version}</version>
-            </dependency>
-
-
-            <dependency>
-                <groupId>com.alibaba</groupId>
-                <artifactId>fastjson</artifactId>
-                <version>${fastjson.version}</version>
-            </dependency>
-
-            <!-- apache commons start -->
-            <dependency>
-                <groupId>org.apache.commons</groupId>
-                <artifactId>commons-lang3</artifactId>
-                <version>${commons-lang3.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.commons</groupId>
-                <artifactId>commons-collections4</artifactId>
-                <version>${commons-collections4.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-net</groupId>
-                <artifactId>commons-net</artifactId>
-                <version>${commons-net.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.apache.commons</groupId>
-                <artifactId>commons-pool2</artifactId>
-                <version>${commons-pool2.version}</version>
-            </dependency>
-            <!-- apache commons end -->
-
-            <dependency>
-                <groupId>org.reflections</groupId>
-                <artifactId>reflections</artifactId>
-                <version>${reflections.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>com.google.guava</groupId>
-                <artifactId>guava</artifactId>
-                <version>${guava.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.fusesource.jansi</groupId>
-                <artifactId>jansi</artifactId>
-                <version>${jansi.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.projectlombok</groupId>
-                <artifactId>lombok</artifactId>
-                <version>${lombok.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.yaml</groupId>
-                <artifactId>snakeyaml</artifactId>
-                <version>${snakeyaml.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>cn.hutool</groupId>
-                <artifactId>hutool-all</artifactId>
-                <version>${hutool.version}</version>
-            </dependency>
-
-            <!-- ini格式处理 -->
-            <dependency>
-                <groupId>org.ini4j</groupId>
-                <artifactId>ini4j</artifactId>
-                <version>${ini4j.version}</version>
-            </dependency>
-
-            <!-- 对象属性复制 https://mapstruct.org/ -->
-            <dependency>
-                <groupId>org.mapstruct</groupId>
-                <artifactId>mapstruct</artifactId>
-                <version>${mapstruct.version}</version>
-            </dependency>
-
-            <!-- XSS -->
-            <dependency>
-                <groupId>org.apache.commons</groupId>
-                <artifactId>commons-text</artifactId>
-                <version>${commons-text.version}</version>
-            </dependency>
-
-            <!-- 项目模块start -->
-            <dependency>
-                <groupId>io.geekidea.springbootplus</groupId>
-                <artifactId>admin</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>io.geekidea.springbootplus</groupId>
-                <artifactId>bootstrap</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>io.geekidea.springbootplus</groupId>
-                <artifactId>config</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>io.geekidea.springbootplus</groupId>
-                <artifactId>example</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>io.geekidea.springbootplus</groupId>
-                <artifactId>framework</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>io.geekidea.springbootplus</groupId>
-                <artifactId>system</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>io.geekidea.springbootplus</groupId>
-                <artifactId>scheduled</artifactId>
-                <version>${project.version}</version>
-            </dependency>
-            <!-- 项目模块end -->
-
-        </dependencies>
-    </dependencyManagement>
-
     <dependencies>
         <dependency>
             <groupId>org.springframework.boot</groupId>
@@ -343,222 +45,169 @@
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-json</artifactId>
+            <artifactId>spring-boot-starter-aop</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-validation</artifactId>
+            <artifactId>spring-boot-starter-data-jdbc</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-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
+
         <dependency>
-            <groupId>com.alibaba</groupId>
-            <artifactId>fastjson</artifactId>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.33</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.5.4.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter-test</artifactId>
+            <version>3.5.4.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-generator</artifactId>
+            <version>3.5.4.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.velocity</groupId>
+            <artifactId>velocity-engine-core</artifactId>
+            <version>2.3</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+            <version>2.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
+            <version>4.3.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>
+            <version>3.14.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-collections4</artifactId>
+            <version>4.4</version>
         </dependency>
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
+            <version>2.15.0</version>
         </dependency>
         <dependency>
             <groupId>commons-codec</groupId>
             <artifactId>commons-codec</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>commons-net</groupId>
-            <artifactId>commons-net</artifactId>
+            <version>1.16.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
-            <artifactId>commons-pool2</artifactId>
+            <artifactId>commons-text</artifactId>
+            <version>1.11.0</version>
         </dependency>
         <dependency>
-            <groupId>org.reflections</groupId>
-            <artifactId>reflections</artifactId>
+            <groupId>com.alibaba.fastjson2</groupId>
+            <artifactId>fastjson2</artifactId>
+            <version>2.0.42</version>
         </dependency>
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
+            <version>32.1.3-jre</version>
         </dependency>
         <dependency>
-            <groupId>org.fusesource.jansi</groupId>
-            <artifactId>jansi</artifactId>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.23</version>
         </dependency>
         <dependency>
-            <groupId>org.projectlombok</groupId>
-            <artifactId>lombok</artifactId>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>3.3.3</version>
         </dependency>
         <dependency>
-            <groupId>org.yaml</groupId>
-            <artifactId>snakeyaml</artifactId>
+            <groupId>org.lionsoul</groupId>
+            <artifactId>ip2region</artifactId>
+            <version>2.7.0</version>
         </dependency>
         <dependency>
-            <groupId>cn.hutool</groupId>
-            <artifactId>hutool-all</artifactId>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <version>24.1.0</version>
+            <scope>compile</scope>
         </dependency>
         <dependency>
-            <groupId>org.ini4j</groupId>
-            <artifactId>ini4j</artifactId>
+            <groupId>com.github.yitter</groupId>
+            <artifactId>yitter-idgenerator</artifactId>
+            <version>1.0.6</version>
         </dependency>
         <dependency>
-            <groupId>org.mapstruct</groupId>
-            <artifactId>mapstruct</artifactId>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>3.17.2</version>
         </dependency>
         <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-text</artifactId>
+            <groupId>com.github.oshi</groupId>
+            <artifactId>oshi-core</artifactId>
+            <version>6.4.1</version>
         </dependency>
+
+
     </dependencies>
 
     <build>
-        <resources>
-            <resource>
-                <directory>src/main/resources</directory>
-                <filtering>true</filtering>
-            </resource>
-        </resources>
-
+        <finalName>${project.artifactId}</finalName>
         <plugins>
             <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-resources-plugin</artifactId>
-                <version>${maven-resources-plugin.version}</version>
-            </plugin>
-
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>${maven-compiler-plugin.version}</version>
-                <configuration>
-                    <source>${java.version}</source>
-                    <target>${java.version}</target>
-                    <annotationProcessorPaths>
-                        <path>
-                            <groupId>org.mapstruct</groupId>
-                            <artifactId>mapstruct-processor</artifactId>
-                            <version>${mapstruct.version}</version>
-                        </path>
-                        <path>
-                            <groupId>org.projectlombok</groupId>
-                            <artifactId>lombok</artifactId>
-                            <version>${lombok.version}</version>
-                        </path>
-                    </annotationProcessorPaths>
-                </configuration>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
             </plugin>
-
             <!-- 打包时跳过测试 -->
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <version>${maven-surefire-plugin.version}</version>
+                <version>2.22.2</version>
                 <configuration>
                     <skipTests>true</skipTests>
                     <testFailureIgnore>true</testFailureIgnore>
                 </configuration>
             </plugin>
-
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-javadoc-plugin</artifactId>
-                <version>${maven-javadoc-plugin.version}</version>
-                <configuration>
-                    <additionalOptions>
-                        <additionalOption>-Xdoclint:none</additionalOption>
-                    </additionalOptions>
-                    <!-- 打包时跳过 -->
-                    <skip>true</skip>
-                </configuration>
-                <executions>
-                    <execution>
-                        <id>attach-javadocs</id>
-                        <goals>
-                            <goal>jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-
-            <!-- 版本统一管理插件 -->
-            <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>versions-maven-plugin</artifactId>
-                <version>${maven-versions-plugin.version}</version>
-            </plugin>
         </plugins>
     </build>
 
-    <!--MAVEN打包选择运行环境-->
-    <!-- dev:开发环境(默认) test:测试环境 uat:用户验收 prod:生产环境 -->
-    <profiles>
-        <profile>
-            <id>dev</id>
-            <properties>
-                <profileActive>dev</profileActive>
-            </properties>
-            <activation>
-                <activeByDefault>true</activeByDefault>
-            </activation>
-        </profile>
-        <profile>
-            <id>test</id>
-            <properties>
-                <profileActive>test</profileActive>
-            </properties>
-            <activation>
-                <activeByDefault>false</activeByDefault>
-            </activation>
-        </profile>
-        <profile>
-            <id>uat</id>
-            <properties>
-                <profileActive>uat</profileActive>
-            </properties>
-            <activation>
-                <activeByDefault>false</activeByDefault>
-            </activation>
-        </profile>
-        <profile>
-            <id>prod</id>
-            <properties>
-                <profileActive>prod</profileActive>
-            </properties>
-            <activation>
-                <activeByDefault>false</activeByDefault>
-            </activation>
-        </profile>
-    </profiles>
-
-    <repositories>
-        <repository>
-            <id>aliyun</id>
-            <name>aliyun-maven</name>
-            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
-        </repository>
-        <repository>
-            <id>spring-milestones</id>
-            <name>Spring Milestones</name>
-            <url>https://maven.aliyun.com/repository/spring</url>
-        </repository>
-        <repository>
-            <id>central</id>
-            <name>maven-central</name>
-            <url>http://central.maven.org/maven2/</url>
-        </repository>
-    </repositories>
-
-</project>
\ No newline at end of file
+</project>
diff --git a/restart-docker.sh b/restart-docker.sh
new file mode 100644
index 00000000..48bc811a
--- /dev/null
+++ b/restart-docker.sh
@@ -0,0 +1,31 @@
+# 以下在服务器上操作
+# 1. 停止容器
+docker stop spring-boot-plus-container
+# 2. 删除容器
+docker rm spring-boot-plus-container
+# 3. 删除镜像
+docker rmi spring-boot-plus
+# 4. 加载镜像
+docker load -i spring-boot-plus.tar
+# 5. 服务器启动docker，将日志打印到外部目录
+# 映射外部日志目录和外部文件上传目录
+# docker run -p 8888:8888 -d  -v /Users/mrliu/geekidea/spring-boot-plus/docker-logs:/spring-boot-plus/logs -v /Users/mrliu/geekidea/spring-boot-plus/docker-upload:/home/spring-boot-plus/upload  --name spring-boot-plus-container spring-boot-plus
+# 直接运行
+docker run -p 8888:8888 -d  --name spring-boot-plus-container spring-boot-plus
+# 6. 查看运行情况
+docker ps -a -f name=spring-boot-plus-container
+# 7. 查看指定名称的镜像
+docker images spring-boot-plus
+# 8. 查看运行日志
+# 实时查看最新前500行日志
+docker logs -f -t --tail 500 spring-boot-plus-container
+# 查看物理主机上映射的日志
+#tail -fn 500 /Users/mrliu/geekidea/spring-boot-plus/docker-logs/boot-info.log
+#将日志全部导出到宿主机指定物理文件
+#docker logs spring-boot-plus-container >> /Users/mrliu/geekidea/spring-boot-plus/docker-logs/spring-boot-plus.log
+# 9. 访问项目，本地运行docker可以用localhost访问，服务器运行docker需将localhost该成docker物理机ip
+# 访问后端接口文档
+# http://localhost:8888/api/doc.html#/home
+
+
+
diff --git a/scheduled/.gitignore b/scheduled/.gitignore
deleted file mode 100644
index ccdf702f..00000000
--- a/scheduled/.gitignore
+++ /dev/null
@@ -1,36 +0,0 @@
-/target/
-/classes
-!.mvn/wrapper/maven-wrapper.jar
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-
-### NetBeans ###
-/nbproject/private/
-/build/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-
-.DS_Store
-
-*.log
-logs
-
-*.rdb
-
-
-
diff --git a/scheduled/README.md b/scheduled/README.md
deleted file mode 100644
index 09e75758..00000000
--- a/scheduled/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# scheduled 项目任务调度模块
\ No newline at end of file
diff --git a/scheduled/pom.xml b/scheduled/pom.xml
deleted file mode 100644
index 270b6edc..00000000
--- a/scheduled/pom.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>io.geekidea.springbootplus</groupId>
-        <artifactId>parent</artifactId>
-        <version>2.0</version>
-    </parent>
-
-    <artifactId>scheduled</artifactId>
-    <name>scheduled</name>
-    <description>任务调度JOB模块</description>
-
-    <dependencies>
-        <dependency>
-            <groupId>io.geekidea.springbootplus</groupId>
-            <artifactId>example</artifactId>
-        </dependency>
-    </dependencies>
-
-</project>
diff --git a/scheduled/src/main/java/io/geekidea/springbootplus/scheduled/HelloScheduled.java b/scheduled/src/main/java/io/geekidea/springbootplus/scheduled/HelloScheduled.java
deleted file mode 100644
index 4a0b0987..00000000
--- a/scheduled/src/main/java/io/geekidea/springbootplus/scheduled/HelloScheduled.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.scheduled;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-/**
- * @author geekidea
- * @date 2020/3/16
- **/
-@Slf4j
-@Component
-public class HelloScheduled {
-
-    /**
-     * 每小时执行一次
-     */
-    @Scheduled(cron = "0 0 0/1 * * ? ")
-    public void hello() throws Exception {
-        log.debug("HelloScheduled...");
-    }
-
-}
diff --git a/src/main/java/io/geekidea/boot/SpringBootPlusApplication.java b/src/main/java/io/geekidea/boot/SpringBootPlusApplication.java
new file mode 100644
index 00000000..cf84650c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/SpringBootPlusApplication.java
@@ -0,0 +1,69 @@
+package io.geekidea.boot;
+
+import io.geekidea.boot.util.IpUtil;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+import java.util.List;
+
+/**
+ * 启动类
+ *
+ * @author geekidea
+ * @date 2022-3-16
+ */
+@EnableAsync
+@SpringBootApplication
+public class SpringBootPlusApplication {
+
+    private static final String BACKSLASH = "/";
+
+    public static void main(String[] args) throws Exception {
+        ConfigurableApplicationContext context = SpringApplication.run(SpringBootPlusApplication.class, args);
+        System.out.println("  _____ _______       _____ _______    _____ _    _  _____ _____ ______  _____ _____ \n" +
+                " / ____|__   __|/\\   |  __ \\__   __|  / ____| |  | |/ ____/ ____|  ____|/ ____/ ____|\n" +
+                "| (___    | |  /  \\  | |__) | | |    | (___ | |  | | |   | |    | |__  | (___| (___  \n" +
+                " \\___ \\   | | / /\\ \\ |  _  /  | |     \\___ \\| |  | | |   | |    |  __|  \\___ \\\\___ \\ \n" +
+                " ____) |  | |/ ____ \\| | \\ \\  | |     ____) | |__| | |___| |____| |____ ____) |___) |\n" +
+                "|_____/   |_/_/    \\_\\_|  \\_\\ |_|    |_____/ \\____/ \\_____\\_____|______|_____/_____/ \n");
+        // 打印项目信息
+        printlnProjectInfo(context);
+    }
+
+    /**
+     * 打印项目信息
+     *
+     * @param context
+     */
+    private static void printlnProjectInfo(ConfigurableApplicationContext context) {
+        try {
+            ConfigurableEnvironment environment = context.getEnvironment();
+            String serverPort = environment.getProperty("server.port");
+            String contextPath = environment.getProperty("server.servlet.context-path");
+            if (!BACKSLASH.equals(contextPath)) {
+                contextPath = contextPath + BACKSLASH;
+            }
+            String localhostDocUrl = "\nhttp://localhost:" + serverPort + contextPath + "doc.html";
+            System.out.println(localhostDocUrl);
+            String localhostSwaggerUrl = "http://localhost:" + serverPort + contextPath + "swagger-ui/index.html";
+            System.out.println(localhostSwaggerUrl);
+            List<String> ipV4List = IpUtil.getLocalhostIpList();
+            if (CollectionUtils.isNotEmpty(ipV4List)) {
+                for (String ip : ipV4List) {
+                    String ipUrl = "http://" + ip + ":" + serverPort + contextPath + "doc.html";
+                    System.out.println(ipUrl);
+                }
+            }
+            System.out.println("\n账号：admin");
+            System.out.println("密码：123456");
+            System.out.println("swagger密码：e10adc3949ba59abbe56e057f20f883e\n");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/annotation/AppUserRole.java b/src/main/java/io/geekidea/boot/auth/annotation/AppUserRole.java
new file mode 100644
index 00000000..9c0e4266
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/annotation/AppUserRole.java
@@ -0,0 +1,24 @@
+package io.geekidea.boot.auth.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 检查App端用户角色
+ *
+ * @author geekidea
+ * @date 2024/1/6
+ **/
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Login
+public @interface AppUserRole {
+
+    /**
+     * 角色ID或角色编码
+     *
+     * @return
+     */
+    String[] value() default {};
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/annotation/DataScope.java b/src/main/java/io/geekidea/boot/auth/annotation/DataScope.java
new file mode 100644
index 00000000..161ac82b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/annotation/DataScope.java
@@ -0,0 +1,32 @@
+package io.geekidea.boot.auth.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 数据权限范围注解
+ *
+ * @author geekidea
+ * @date 2023/12/02
+ **/
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataScope {
+
+    // TODO 如果是商城，则可新增商家表别名，和对应的筛选列，如果有部门数据权限，则新增部门表和列别名
+
+    /**
+     * 用户表别名
+     *
+     * @return
+     */
+    String userAlias() default "";
+
+    /**
+     * 筛选的userId列
+     *
+     * @return
+     */
+    String userIdColumn() default "id";
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/annotation/IgnoreLogin.java b/src/main/java/io/geekidea/boot/auth/annotation/IgnoreLogin.java
new file mode 100644
index 00000000..a16b8643
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/annotation/IgnoreLogin.java
@@ -0,0 +1,16 @@
+package io.geekidea.boot.auth.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 忽略登录的注解
+ *
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface IgnoreLogin {
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/annotation/Login.java b/src/main/java/io/geekidea/boot/auth/annotation/Login.java
new file mode 100644
index 00000000..1420eb77
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/annotation/Login.java
@@ -0,0 +1,16 @@
+package io.geekidea.boot.auth.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 需要登录的注解
+ *
+ * @author geekidea
+ * @date 2023/11/22
+ **/
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Login {
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/annotation/Permission.java b/src/main/java/io/geekidea/boot/auth/annotation/Permission.java
new file mode 100644
index 00000000..3da10e76
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/annotation/Permission.java
@@ -0,0 +1,37 @@
+package io.geekidea.boot.auth.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 权限注解
+ *
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Permission {
+
+    /**
+     * 权限编码
+     *
+     * @return
+     */
+    String value();
+
+    /**
+     * 角色编码
+     *
+     * @return
+     */
+    String role() default "";
+
+    /**
+     * 角色编码数组
+     *
+     * @return
+     */
+    String[] roles() default {};
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/aop/AppDataRangeAop.java b/src/main/java/io/geekidea/boot/auth/aop/AppDataRangeAop.java
new file mode 100644
index 00000000..ed9e6a76
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/aop/AppDataRangeAop.java
@@ -0,0 +1,40 @@
+package io.geekidea.boot.auth.aop;
+
+import io.geekidea.boot.common.constant.AopConstant;
+import io.geekidea.boot.framework.query.DataRangeQuery;
+import io.geekidea.boot.util.DataRangeUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ArrayUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author geekidea
+ * @date 2022/4/20
+ **/
+@Slf4j
+@Aspect
+@Component
+@ConditionalOnProperty(name = "login.app.enable", havingValue = "true", matchIfMissing = true)
+public class AppDataRangeAop {
+
+    @Around(AopConstant.APP_POINTCUT)
+    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
+        Object[] args = joinPoint.getArgs();
+        if (ArrayUtils.isEmpty(args)) {
+            return joinPoint.proceed();
+        }
+        for (Object arg : args) {
+            if (arg instanceof DataRangeQuery) {
+                DataRangeQuery dataRangeQuery = (DataRangeQuery) arg;
+                DataRangeUtil.handleAppLogin(dataRangeQuery);
+                break;
+            }
+        }
+        return joinPoint.proceed();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/aop/CommonDataRangeAop.java b/src/main/java/io/geekidea/boot/auth/aop/CommonDataRangeAop.java
new file mode 100644
index 00000000..c8cd772c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/aop/CommonDataRangeAop.java
@@ -0,0 +1,39 @@
+package io.geekidea.boot.auth.aop;
+
+import io.geekidea.boot.common.constant.AopConstant;
+import io.geekidea.boot.framework.query.DataRangeQuery;
+import io.geekidea.boot.util.DataRangeUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ArrayUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author geekidea
+ * @date 2022/4/20
+ **/
+@Slf4j
+@Aspect
+@Component
+public class CommonDataRangeAop {
+
+    @Around(AopConstant.COMMON_POINTCUT)
+    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
+        Object[] args = joinPoint.getArgs();
+        if (ArrayUtils.isEmpty(args)) {
+            return joinPoint.proceed();
+        }
+        for (Object arg : args) {
+            if (arg instanceof DataRangeQuery) {
+                DataRangeQuery dataRangeQuery = (DataRangeQuery) arg;
+                DataRangeUtil.handleCommonLogin(dataRangeQuery);
+                break;
+            }
+        }
+        return joinPoint.proceed();
+    }
+
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/aop/DataRangeAop.java b/src/main/java/io/geekidea/boot/auth/aop/DataRangeAop.java
new file mode 100644
index 00000000..e1efd4c4
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/aop/DataRangeAop.java
@@ -0,0 +1,41 @@
+package io.geekidea.boot.auth.aop;
+
+import io.geekidea.boot.common.constant.AopConstant;
+import io.geekidea.boot.framework.query.DataRangeQuery;
+import io.geekidea.boot.util.DataRangeUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ArrayUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author geekidea
+ * @date 2022/4/20
+ **/
+@Slf4j
+@Aspect
+@Component
+@ConditionalOnProperty(name = "login.admin.enable", havingValue = "true", matchIfMissing = true)
+public class DataRangeAop {
+
+    @Around(AopConstant.ADMIN_POINTCUT)
+    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
+        Object[] args = joinPoint.getArgs();
+        if (ArrayUtils.isEmpty(args)) {
+            return joinPoint.proceed();
+        }
+        for (Object arg : args) {
+            if (arg instanceof DataRangeQuery) {
+                DataRangeQuery dataRangeQuery = (DataRangeQuery) arg;
+                DataRangeUtil.handleAdminLogin(dataRangeQuery);
+                break;
+            }
+        }
+        return joinPoint.proceed();
+    }
+
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/cache/AppLoginCache.java b/src/main/java/io/geekidea/boot/auth/cache/AppLoginCache.java
new file mode 100644
index 00000000..e686f5c6
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/cache/AppLoginCache.java
@@ -0,0 +1,44 @@
+package io.geekidea.boot.auth.cache;
+
+import io.geekidea.boot.auth.vo.AppLoginVo;
+
+/**
+ * 在当前线程中缓存token
+ * 如果开启多线程需要获取
+ *
+ * @author geekidea
+ * @date 2023/12/7
+ **/
+public class AppLoginCache {
+
+    /**
+     * 当前线程中保存APP移动端登录信息
+     */
+    private static final ThreadLocal<AppLoginVo> APP_LOGIN_CACHE = new ThreadLocal<>();
+
+    /**
+     * 设置APP移动端登录信息到当前线程中
+     *
+     * @param appLoginVo
+     */
+    public static void set(AppLoginVo appLoginVo) {
+        APP_LOGIN_CACHE.set(appLoginVo);
+    }
+
+    /**
+     * 从当前线程获取APP移动端登录信息
+     *
+     * @return
+     */
+    public static AppLoginVo get() {
+        return APP_LOGIN_CACHE.get();
+    }
+
+    /**
+     * 从当前线程中移除APP移动端登录信息
+     */
+    public static void remove() {
+        APP_LOGIN_CACHE.remove();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/cache/LoginCache.java b/src/main/java/io/geekidea/boot/auth/cache/LoginCache.java
new file mode 100644
index 00000000..4d554307
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/cache/LoginCache.java
@@ -0,0 +1,44 @@
+package io.geekidea.boot.auth.cache;
+
+import io.geekidea.boot.auth.vo.LoginVo;
+
+/**
+ * 在当前线程中缓存管理后台登录信息
+ * 如果开启多线程需要获取
+ *
+ * @author geekidea
+ * @date 2023/12/7
+ **/
+public class LoginCache {
+
+    /**
+     * 当前线程中保存管理后台登录信息
+     */
+    private static final ThreadLocal<LoginVo> LOGIN_CACHE = new ThreadLocal<>();
+
+    /**
+     * 设置管理后台登录信息到当前线程中
+     *
+     * @param loginVo
+     */
+    public static void set(LoginVo loginVo) {
+        LOGIN_CACHE.set(loginVo);
+    }
+
+    /**
+     * 从当前线程获取管理后台登录信息
+     *
+     * @return
+     */
+    public static LoginVo get() {
+        return LOGIN_CACHE.get();
+    }
+
+    /**
+     * 从当前线程中移除管理后台登录信息
+     */
+    public static void remove() {
+        LOGIN_CACHE.remove();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/cache/TokenCache.java b/src/main/java/io/geekidea/boot/auth/cache/TokenCache.java
new file mode 100644
index 00000000..93b19bbe
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/cache/TokenCache.java
@@ -0,0 +1,42 @@
+package io.geekidea.boot.auth.cache;
+
+/**
+ * 在当前线程中缓存token
+ * 如果开启多线程需要获取
+ *
+ * @author geekidea
+ * @date 2023/12/7
+ **/
+public class TokenCache {
+
+    /**
+     * 当前线程中保存token值
+     */
+    private static final ThreadLocal<String> TOKEN_CACHE = new ThreadLocal<>();
+
+    /**
+     * 设置token值到当前线程中
+     *
+     * @param token
+     */
+    public static void set(String token) {
+        TOKEN_CACHE.set(token);
+    }
+
+    /**
+     * 从当前线程获取token值
+     *
+     * @return
+     */
+    public static String get() {
+        return TOKEN_CACHE.get();
+    }
+
+    /**
+     * 从当前线程中移除token值
+     */
+    public static void remove() {
+        TOKEN_CACHE.remove();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/controller/AppLoginController.java b/src/main/java/io/geekidea/boot/auth/controller/AppLoginController.java
new file mode 100644
index 00000000..8fb8f508
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/controller/AppLoginController.java
@@ -0,0 +1,117 @@
+package io.geekidea.boot.auth.controller;
+
+import io.geekidea.boot.auth.annotation.Login;
+import io.geekidea.boot.auth.dto.AppAccountLoginDto;
+import io.geekidea.boot.auth.dto.AppLoginDto;
+import io.geekidea.boot.auth.service.AppLoginService;
+import io.geekidea.boot.auth.vo.AppLoginVo;
+import io.geekidea.boot.auth.vo.LoginTokenVo;
+import io.geekidea.boot.common.constant.LoginConstant;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.util.CookieUtil;
+import io.geekidea.boot.util.HttpServletRequestUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Slf4j
+@RestController
+@Tag(name = "APP登录")
+@RequestMapping("/app")
+public class AppLoginController {
+
+    @Autowired
+    private AppLoginService appLoginService;
+
+    /**
+     * APP小程序登录
+     *
+     * @param appLoginDto
+     * @param response
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/login")
+    @Operation(summary = "APP小程序登录")
+    public ApiResult<LoginTokenVo> login(@Valid @RequestBody AppLoginDto appLoginDto, HttpServletRequest request, HttpServletResponse response) {
+        LoginTokenVo loginTokenVo = appLoginService.login(appLoginDto);
+        // 输出token到cookie
+        addCookie(loginTokenVo.getToken(), request, response);
+        return ApiResult.success(loginTokenVo);
+    }
+
+    /**
+     * APP账号密码登录
+     *
+     * @param loginAccountAppDto
+     * @param response
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/accountLogin")
+    @Operation(summary = "APP账号密码登录")
+    public ApiResult<LoginTokenVo> accountLogin(@Valid @RequestBody AppAccountLoginDto loginAccountAppDto, HttpServletRequest request, HttpServletResponse response) {
+        LoginTokenVo loginTokenVo = appLoginService.accountLogin(loginAccountAppDto);
+        // 输出token到cookie
+        addCookie(loginTokenVo.getToken(), request, response);
+        return ApiResult.success(loginTokenVo);
+    }
+
+    /**
+     * 获取APP登录用户信息
+     *
+     * @return
+     * @throws Exception
+     */
+    @Login
+    @PostMapping("/getLoginUserInfo")
+    @Operation(summary = "获取APP登录用户信息")
+    public ApiResult<AppLoginVo> getLoginUserInfo() {
+        AppLoginVo appLoginVo = appLoginService.getLoginUserInfo();
+        return ApiResult.success(appLoginVo);
+    }
+
+    /**
+     * APP退出
+     *
+     * @return
+     * @throws Exception
+     */
+    @Login
+    @PostMapping("/logout")
+    @Operation(summary = "APP退出")
+    public ApiResult<Boolean> logout(HttpServletRequest request, HttpServletResponse response) {
+        appLoginService.logout();
+        // 从cookie中删除token
+        CookieUtil.deleteCookie(LoginConstant.APP_COOKIE_TOKEN_NAME, request, response);
+        return ApiResult.success();
+    }
+
+    /**
+     * 输出token到cookie
+     *
+     * @param token
+     * @param request
+     * @param response
+     */
+    private void addCookie(String token, HttpServletRequest request, HttpServletResponse response) {
+        boolean docRequest = HttpServletRequestUtil.isDocRequest(request);
+        if (docRequest) {
+            CookieUtil.addCookie(LoginConstant.APP_COOKIE_TOKEN_NAME, token, request, response);
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/controller/LoginController.java b/src/main/java/io/geekidea/boot/auth/controller/LoginController.java
new file mode 100644
index 00000000..fe3953d3
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/controller/LoginController.java
@@ -0,0 +1,83 @@
+package io.geekidea.boot.auth.controller;
+
+import io.geekidea.boot.auth.dto.LoginDto;
+import io.geekidea.boot.auth.service.LoginService;
+import io.geekidea.boot.auth.vo.LoginTokenVo;
+import io.geekidea.boot.auth.vo.LoginVo;
+import io.geekidea.boot.common.constant.LoginConstant;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.util.CookieUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+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 javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Slf4j
+@RestController
+@Tag(name = "管理后台登录")
+@RequestMapping("/admin")
+public class LoginController {
+
+    @Autowired
+    private LoginService loginService;
+
+    /**
+     * 管理后台登录
+     *
+     * @param loginDto
+     * @param request
+     * @param response
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/login")
+    @Operation(summary = "管理后台登录")
+    public ApiResult<LoginTokenVo> login(@Valid @RequestBody LoginDto loginDto, HttpServletRequest request, HttpServletResponse response) {
+        LoginTokenVo loginTokenVo = loginService.login(loginDto);
+        // 输出token到cookie
+        CookieUtil.addCookie(LoginConstant.ADMIN_COOKIE_TOKEN_NAME, loginTokenVo.getToken(), request, response);
+        return ApiResult.success(loginTokenVo);
+    }
+
+    /**
+     * 获取管理后台登录用户信息
+     *
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getLoginUserInfo")
+    @Operation(summary = "获取管理后台登录用户信息")
+    public ApiResult<LoginVo> getLoginUserInfo() {
+        LoginVo loginVo = loginService.getLoginUserInfo();
+        return ApiResult.success(loginVo);
+    }
+
+    /**
+     * 管理后台退出
+     *
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/logout")
+    @Operation(summary = "管理后台退出")
+    public ApiResult<Boolean> logout(HttpServletRequest request, HttpServletResponse response) {
+        // 删除缓存
+        loginService.logout();
+        // 从cookie中删除token
+        CookieUtil.deleteCookie(LoginConstant.ADMIN_COOKIE_TOKEN_NAME, request, response);
+        return ApiResult.success();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/dto/AppAccountLoginDto.java b/src/main/java/io/geekidea/boot/auth/dto/AppAccountLoginDto.java
new file mode 100644
index 00000000..7dd2ffa4
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/dto/AppAccountLoginDto.java
@@ -0,0 +1,24 @@
+package io.geekidea.boot.auth.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Data
+@Schema(description = "AccountLoginAppDto")
+public class AppAccountLoginDto {
+
+    @Schema(description = "用户名", example = "boot")
+    @NotBlank(message = "用户名不能为空")
+    private String username;
+
+    @Schema(description = "密码", example = "e10adc3949ba59abbe56e057f20f883e")
+    @NotBlank(message = "密码不能为空")
+    private String password;
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/dto/AppLoginDto.java b/src/main/java/io/geekidea/boot/auth/dto/AppLoginDto.java
new file mode 100644
index 00000000..89d5e475
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/dto/AppLoginDto.java
@@ -0,0 +1,20 @@
+package io.geekidea.boot.auth.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Data
+@Schema(description = "LoginAppDto")
+public class AppLoginDto {
+
+    @Schema(description = "小程序code")
+    @NotBlank(message = "小程序code不能为空")
+    private String code;
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/dto/LoginDto.java b/src/main/java/io/geekidea/boot/auth/dto/LoginDto.java
new file mode 100644
index 00000000..f495eb6b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/dto/LoginDto.java
@@ -0,0 +1,24 @@
+package io.geekidea.boot.auth.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Data
+@Schema(description = "AdminLoginDto")
+public class LoginDto {
+
+    @Schema(description = "用户名", example = "admin")
+    @NotBlank(message = "用户名不能为空")
+    private String username;
+
+    @Schema(description = "密码", example = "e10adc3949ba59abbe56e057f20f883e")
+    @NotBlank(message = "密码不能为空")
+    private String password;
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/enums/LoginInterceptStrategy.java b/src/main/java/io/geekidea/boot/auth/enums/LoginInterceptStrategy.java
new file mode 100644
index 00000000..d59052de
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/enums/LoginInterceptStrategy.java
@@ -0,0 +1,64 @@
+package io.geekidea.boot.auth.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 请求路径登录校验拦截策略
+ * 默认拦截包含路径开头的所有请求(/app/**)，在不需要验证登录的controller方法加上@IgnoreLogin，表示此方法不需要登录就能调用
+ * 默认不拦截包含路径开头的所有请求(/app/**)，在需要登录的controller方法上加上注解@Login，表示此方法必须登录才能调用
+ * @author geekidea
+ * @date 2024/1/31
+ **/
+public enum LoginInterceptStrategy {
+    LOGIN(1, "需要登录"),
+    IGNORE_LOGIN(2, "忽略登录");
+
+    private Integer code;
+    private String desc;
+
+    LoginInterceptStrategy(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, LoginInterceptStrategy> map = new HashMap<>();
+
+    static {
+        for (LoginInterceptStrategy type : values()) {
+            map.put(type.code, type);
+        }
+    }
+
+    public static Integer getCode(LoginInterceptStrategy type) {
+        if (type == null) {
+            return null;
+        }
+        return type.code;
+    }
+
+    public static LoginInterceptStrategy get(Integer code) {
+        LoginInterceptStrategy type = map.get(code);
+        if (type == null) {
+            return null;
+        }
+        return type;
+    }
+
+    public static String getDesc(Integer code) {
+        LoginInterceptStrategy type = get(code);
+        if (type == null) {
+            return null;
+        }
+        return type.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/interceptor/AppLoginInterceptor.java b/src/main/java/io/geekidea/boot/auth/interceptor/AppLoginInterceptor.java
new file mode 100644
index 00000000..def9c296
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/interceptor/AppLoginInterceptor.java
@@ -0,0 +1,100 @@
+package io.geekidea.boot.auth.interceptor;
+
+import io.geekidea.boot.auth.annotation.AppUserRole;
+import io.geekidea.boot.auth.cache.AppLoginCache;
+import io.geekidea.boot.auth.enums.LoginInterceptStrategy;
+import io.geekidea.boot.auth.util.AppLoginUtil;
+import io.geekidea.boot.auth.util.TokenUtil;
+import io.geekidea.boot.auth.vo.AppLoginVo;
+import io.geekidea.boot.config.properties.LoginAppProperties;
+import io.geekidea.boot.framework.exception.AuthException;
+import io.geekidea.boot.framework.exception.LoginTokenException;
+import io.geekidea.boot.framework.interceptor.BaseExcludeMethodInterceptor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.method.HandlerMethod;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Slf4j
+public class AppLoginInterceptor extends BaseExcludeMethodInterceptor {
+
+    @Autowired
+    private LoginAppProperties loginAppProperties;
+
+    @Override
+    protected boolean preHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
+        // 获取token
+        String token = TokenUtil.getToken();
+        AppLoginVo appLoginVo = null;
+        if (StringUtils.isNotBlank(token)) {
+            // 获取登录用户信息
+            appLoginVo = AppLoginUtil.getLoginVo(token);
+            if (appLoginVo != null) {
+                // 将APP移动端的登录信息保存到当前线程中
+                AppLoginCache.set(appLoginVo);
+            }
+        }
+        // 判断登录校验策略
+        LoginInterceptStrategy loginInterceptStrategy = loginAppProperties.getLoginInterceptStrategy();
+        if (LoginInterceptStrategy.LOGIN == loginInterceptStrategy) {
+            // 默认都需要登录，此时判断是否有@IgnoreLogin注解，如果有，则跳过，否则，则验证登录
+            boolean existsIgnoreLoginAnnotation = existsIgnoreLoginAnnotation(handlerMethod);
+            if (existsIgnoreLoginAnnotation) {
+                return true;
+            }
+            // 是否是排除登录的路径，如果是，则跳过，否则，则验证
+            boolean isIgnoreLoginPath = isIgnoreLoginPath(request, loginAppProperties.getIgnoreLoginPaths());
+            if (isIgnoreLoginPath) {
+                return true;
+            }
+        } else {
+            // 默认都忽略登录，此时判断是否有@Login注解，如果有，则验证登录，否则，则跳过
+            boolean existsLoginAnnotation = existsLoginAnnotation(handlerMethod);
+            if (!existsLoginAnnotation) {
+                // 如果不是要校验的路径，则跳过
+                boolean isCheckLoginPath = isCheckLoginPath(request, loginAppProperties.getCheckLoginPaths());
+                if (!isCheckLoginPath) {
+                    return true;
+                }
+            }
+        }
+        // 登录校验
+        if (StringUtils.isBlank(token)) {
+            throw new LoginTokenException("请登录后再操作");
+        }
+        // 校验登录用户信息
+        if (appLoginVo == null) {
+            throw new LoginTokenException("登录已过期或登录信息不存在，请重新登录");
+        }
+        // 校验用户角色
+        AppUserRole appUserRoleAnnotation = getAppUserRoleAnnotation(handlerMethod);
+        if (appUserRoleAnnotation != null) {
+            String[] values = appUserRoleAnnotation.value();
+            if (ArrayUtils.isNotEmpty(values)) {
+                Long userRoleId = AppLoginUtil.getUserRoleId();
+                String userRoleCode = AppLoginUtil.getUserRoleCode();
+                // 如果方法上的角色ID或角色编码与当前登录用户一致，则放行，否则，跳过
+                if (ArrayUtils.contains(values, userRoleId) || ArrayUtils.contains(values, userRoleCode)) {
+                    return true;
+                } else {
+                    throw new AuthException("当前用户没有权限访问");
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+        AppLoginCache.remove();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/interceptor/CommonLoginInterceptor.java b/src/main/java/io/geekidea/boot/auth/interceptor/CommonLoginInterceptor.java
new file mode 100644
index 00000000..6c4dce73
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/interceptor/CommonLoginInterceptor.java
@@ -0,0 +1,78 @@
+package io.geekidea.boot.auth.interceptor;
+
+import io.geekidea.boot.auth.cache.AppLoginCache;
+import io.geekidea.boot.auth.cache.LoginCache;
+import io.geekidea.boot.auth.util.AppLoginUtil;
+import io.geekidea.boot.auth.util.LoginUtil;
+import io.geekidea.boot.auth.util.TokenUtil;
+import io.geekidea.boot.auth.vo.AppLoginVo;
+import io.geekidea.boot.auth.vo.LoginVo;
+import io.geekidea.boot.common.enums.SystemType;
+import io.geekidea.boot.framework.exception.LoginTokenException;
+import io.geekidea.boot.framework.interceptor.BaseExcludeMethodInterceptor;
+import io.geekidea.boot.util.SystemTypeUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.method.HandlerMethod;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Slf4j
+public class CommonLoginInterceptor extends BaseExcludeMethodInterceptor {
+
+    @Override
+    protected boolean preHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
+        // 获取token
+        String token = TokenUtil.getToken();
+        SystemType systemType = null;
+        if (StringUtils.isNotBlank(token)) {
+            systemType = SystemTypeUtil.getSystemTypeByToken(token);
+            if (SystemType.ADMIN == systemType) {
+                // 获取管理后台登录用户信息
+                LoginVo loginVo = LoginUtil.getLoginVo(token);
+                if (loginVo != null) {
+                    // 将管理后台的登录信息保存到当前线程中
+                    LoginCache.set(loginVo);
+                }
+            } else if (SystemType.APP == systemType) {
+                AppLoginVo loginVo = AppLoginUtil.getLoginVo(token);
+                if (loginVo != null) {
+                    // 将APP移动端的登录信息保存到当前线程中
+                    AppLoginCache.set(loginVo);
+                }
+            }
+        }
+        // 如果不存在@Login注解，则跳过
+        boolean existsLoginAnnotation = existsLoginAnnotation(handlerMethod);
+        if (!existsLoginAnnotation) {
+            return true;
+        }
+        if (StringUtils.isBlank(token)) {
+            throw new LoginTokenException("请登录后再操作");
+        }
+        if (SystemType.ADMIN == systemType) {
+            // 获取管理后台登录用户信息
+            LoginVo loginVo = LoginCache.get();
+            if (loginVo == null) {
+                throw new LoginTokenException("登录已过期或登录信息不存在，请重新登录");
+            }
+        } else if (SystemType.APP == systemType) {
+            AppLoginVo loginVo = AppLoginCache.get();
+            if (loginVo == null) {
+                throw new LoginTokenException("登录已过期或登录信息不存在，请重新登录");
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+        LoginCache.remove();
+        AppLoginCache.remove();
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/auth/interceptor/ExcludePathInterceptor.java b/src/main/java/io/geekidea/boot/auth/interceptor/ExcludePathInterceptor.java
new file mode 100644
index 00000000..72da854b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/interceptor/ExcludePathInterceptor.java
@@ -0,0 +1,45 @@
+package io.geekidea.boot.auth.interceptor;
+
+import io.geekidea.boot.common.constant.CommonConstant;
+import io.geekidea.boot.config.properties.BootProperties;
+import io.geekidea.boot.framework.interceptor.BaseMethodInterceptor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.web.method.HandlerMethod;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 排除路径拦截器
+ *
+ * @author geekidea
+ * @date 2023/12/03
+ **/
+@Slf4j
+public class ExcludePathInterceptor extends BaseMethodInterceptor {
+
+    @Autowired
+    private BootProperties bootProperties;
+
+    @Override
+    protected boolean preHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
+        String servletPath = request.getServletPath();
+        List<String> excludePaths = bootProperties.getExcludePaths();
+        if (CollectionUtils.isNotEmpty(excludePaths)) {
+            for (String excludePath : excludePaths) {
+                AntPathMatcher antPathMatcher = new AntPathMatcher();
+                boolean match = antPathMatcher.match(excludePath, servletPath);
+                if (match) {
+                    request.setAttribute(CommonConstant.REQUEST_PARAM_EXCLUDE_PATH, true);
+                    break;
+                }
+            }
+        }
+        return true;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/interceptor/LoginInterceptor.java b/src/main/java/io/geekidea/boot/auth/interceptor/LoginInterceptor.java
new file mode 100644
index 00000000..00c67a4f
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/interceptor/LoginInterceptor.java
@@ -0,0 +1,92 @@
+package io.geekidea.boot.auth.interceptor;
+
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.auth.cache.LoginCache;
+import io.geekidea.boot.auth.util.LoginUtil;
+import io.geekidea.boot.auth.util.TokenUtil;
+import io.geekidea.boot.auth.vo.LoginVo;
+import io.geekidea.boot.config.properties.LoginAdminProperties;
+import io.geekidea.boot.framework.exception.LoginException;
+import io.geekidea.boot.framework.exception.LoginTokenException;
+import io.geekidea.boot.framework.interceptor.BaseExcludeMethodInterceptor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.method.HandlerMethod;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Slf4j
+public class LoginInterceptor extends BaseExcludeMethodInterceptor {
+
+    @Autowired
+    private LoginAdminProperties loginAdminProperties;
+
+    @Override
+    protected boolean preHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
+        // 如果token
+        String token = TokenUtil.getToken();
+        LoginVo loginVo = null;
+        if (StringUtils.isNotBlank(token)) {
+            // 获取登录用户信息
+            loginVo = LoginUtil.getLoginVo(token);
+            if (loginVo != null) {
+                // 将管理后台的登录信息保存到当前线程中
+                LoginCache.set(loginVo);
+            }
+        }
+        // 判断是否存在@IgnoreLogin，存在，则跳过
+        boolean existsIgnoreLoginAnnotation = existsIgnoreLoginAnnotation(handlerMethod);
+        if (existsIgnoreLoginAnnotation) {
+            return true;
+        }
+        // 校验登录
+        if (StringUtils.isBlank(token)) {
+            throw new LoginTokenException("请登录后再操作");
+        }
+        // 校验登录用户信息
+        if (loginVo == null) {
+            throw new LoginTokenException("登录已过期或登录信息不存在，请重新登录");
+        }
+        // 获取登录信息判断
+        String roleCode = loginVo.getRoleCode();
+        boolean loginPermission = loginAdminProperties.isLoginPermission();
+        boolean admin = loginVo.isAdmin();
+        if (!admin && loginPermission) {
+            Permission permission = handlerMethod.getMethodAnnotation(Permission.class);
+            if (permission != null) {
+                // 从redis中获取权限列表
+                List<String> permissions = loginVo.getPermissions();
+                if (CollectionUtils.isEmpty(permissions)) {
+                    throw new LoginException("当前用户未设置权限");
+                }
+                String value = permission.value();
+                String role = permission.role();
+                if (!permissions.contains(value)) {
+                    log.error("没有访问权限的登录用户：" + loginVo);
+                    throw new LoginException("没有访问权限");
+                }
+                if (StringUtils.isNotBlank(role)) {
+                    if (roleCode.equals(role)) {
+                        log.error("没有访问权限的登录用户：" + loginVo);
+                        throw new LoginException("该角色没有访问权限");
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+        LoginCache.remove();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/interceptor/NotAuthInterceptor.java b/src/main/java/io/geekidea/boot/auth/interceptor/NotAuthInterceptor.java
new file mode 100644
index 00000000..c6c3c2a1
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/interceptor/NotAuthInterceptor.java
@@ -0,0 +1,26 @@
+package io.geekidea.boot.auth.interceptor;
+
+import io.geekidea.boot.framework.exception.NotAuthException;
+import io.geekidea.boot.framework.interceptor.BaseExcludeMethodInterceptor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.method.HandlerMethod;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 没权限拦截器，项目演示用，生成项目根据情况而定
+ *
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Slf4j
+public class NotAuthInterceptor extends BaseExcludeMethodInterceptor {
+
+    @Override
+    protected boolean preHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
+        log.info("演示环境没有权限访问的url：" + request.getServletPath());
+        throw new NotAuthException("演示环境不允许此操作");
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/interceptor/RefreshTokenInterceptor.java b/src/main/java/io/geekidea/boot/auth/interceptor/RefreshTokenInterceptor.java
new file mode 100644
index 00000000..1cafbd96
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/interceptor/RefreshTokenInterceptor.java
@@ -0,0 +1,39 @@
+package io.geekidea.boot.auth.interceptor;
+
+import io.geekidea.boot.auth.service.AppLoginRedisService;
+import io.geekidea.boot.auth.service.LoginRedisService;
+import io.geekidea.boot.common.enums.SystemType;
+import io.geekidea.boot.framework.interceptor.BaseExcludeMethodInterceptor;
+import io.geekidea.boot.util.SystemTypeUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.method.HandlerMethod;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Slf4j
+public class RefreshTokenInterceptor extends BaseExcludeMethodInterceptor {
+
+    @Autowired
+    private LoginRedisService loginRedisService;
+
+    @Autowired
+    private AppLoginRedisService appLoginRedisService;
+
+    @Override
+    protected boolean preHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
+        SystemType systemType = SystemTypeUtil.getSystemTypeByPath(request);
+        if (SystemType.ADMIN == systemType) {
+            loginRedisService.refreshToken();
+        } else if (SystemType.APP == systemType) {
+            appLoginRedisService.refreshToken();
+        }
+        return true;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/interceptor/TokenInterceptor.java b/src/main/java/io/geekidea/boot/auth/interceptor/TokenInterceptor.java
new file mode 100644
index 00000000..d7a393cf
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/interceptor/TokenInterceptor.java
@@ -0,0 +1,39 @@
+package io.geekidea.boot.auth.interceptor;
+
+import io.geekidea.boot.auth.cache.TokenCache;
+import io.geekidea.boot.auth.util.TokenUtil;
+import io.geekidea.boot.framework.interceptor.BaseExcludeMethodInterceptor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.method.HandlerMethod;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Token拦截器
+ *
+ * @author geekidea
+ * @date 2023/12/03
+ **/
+@Slf4j
+public class TokenInterceptor extends BaseExcludeMethodInterceptor {
+
+    @Override
+    protected boolean preHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
+        // 获取token
+        String token = TokenUtil.getToken(request);
+        if (StringUtils.isBlank(token)) {
+            return true;
+        }
+        // 设置token值到当前线程中，避免重复获取
+        TokenCache.set(token);
+        return true;
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+        TokenCache.remove();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/service/AppLoginRedisService.java b/src/main/java/io/geekidea/boot/auth/service/AppLoginRedisService.java
new file mode 100644
index 00000000..263b2ac2
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/service/AppLoginRedisService.java
@@ -0,0 +1,61 @@
+package io.geekidea.boot.auth.service;
+
+import io.geekidea.boot.auth.vo.AppLoginVo;
+
+/**
+ * @author geekidea
+ * @date 2022/7/12
+ **/
+public interface AppLoginRedisService {
+
+    /**
+     * 获取登录用户token的redis key
+     *
+     * @param token
+     * @return
+     * @throws Exception
+     */
+    String getLoginRedisKey(String token);
+
+    /**
+     * 设置登录用户信息到redis
+     *
+     * @param token
+     * @param appLoginVo
+     * @throws Exception
+     */
+    void setLoginVo(String token, AppLoginVo appLoginVo);
+
+    /**
+     * 获取redis中的登录用户信息
+     *
+     * @param token
+     * @return
+     * @throws Exception
+     */
+    AppLoginVo getLoginVo(String token);
+
+    /**
+     * 删除redis中的登录用户信息
+     *
+     * @param token
+     * @throws Exception
+     */
+    void deleteLoginVo(String token);
+
+    /**
+     * 刷新token
+     *
+     * @throws Exception
+     */
+    void refreshToken();
+
+    /**
+     * 通过用户token删除当前用户之前的所有的redis登录信息
+     *
+     * @param token
+     * @throws Exception
+     */
+    void deleteLoginInfoByToken(String token);
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/service/AppLoginService.java b/src/main/java/io/geekidea/boot/auth/service/AppLoginService.java
new file mode 100644
index 00000000..12534351
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/service/AppLoginService.java
@@ -0,0 +1,70 @@
+package io.geekidea.boot.auth.service;
+
+import io.geekidea.boot.auth.dto.AppAccountLoginDto;
+import io.geekidea.boot.auth.dto.AppLoginDto;
+import io.geekidea.boot.auth.vo.AppLoginVo;
+import io.geekidea.boot.auth.vo.LoginTokenVo;
+import io.geekidea.boot.user.entity.User;
+
+import java.util.Date;
+
+/**
+ * @author geekidea
+ * @date 2022/7/5
+ **/
+public interface AppLoginService {
+
+    /**
+     * APP小程序登录
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    LoginTokenVo login(AppLoginDto dto);
+
+    /**
+     * APP账号密码登录
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    LoginTokenVo accountLogin(AppAccountLoginDto dto);
+
+    /**
+     * APP登录
+     *
+     * @param user
+     * @return
+     * @throws Exception
+     */
+    LoginTokenVo login(User user);
+
+    /**
+     * 刷新登录信息
+     *
+     * @param user
+     * @param token
+     * @param lastLoginTime
+     * @return
+     * @throws Exception
+     */
+    AppLoginVo refreshLoginInfo(User user, String token, Date lastLoginTime);
+
+    /**
+     * 获取登录用户信息
+     *
+     * @return
+     * @throws Exception
+     */
+    AppLoginVo getLoginUserInfo();
+
+    /**
+     * 登出
+     *
+     * @throws Exception
+     */
+    void logout();
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/service/LoginRedisService.java b/src/main/java/io/geekidea/boot/auth/service/LoginRedisService.java
new file mode 100644
index 00000000..5c69d150
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/service/LoginRedisService.java
@@ -0,0 +1,61 @@
+package io.geekidea.boot.auth.service;
+
+import io.geekidea.boot.auth.vo.LoginVo;
+
+/**
+ * @author geekidea
+ * @date 2022/7/12
+ **/
+public interface LoginRedisService {
+
+    /**
+     * 获取登录用户token的redis key
+     *
+     * @param token
+     * @return
+     * @throws Exception
+     */
+    String getLoginRedisKey(String token);
+
+    /**
+     * 设置登录用户信息到redis
+     *
+     * @param token
+     * @param loginVo
+     * @throws Exception
+     */
+    void setLoginVo(String token, LoginVo loginVo);
+
+    /**
+     * 获取redis中的登录用户信息
+     *
+     * @param token
+     * @return
+     * @throws Exception
+     */
+    LoginVo getLoginVo(String token);
+
+    /**
+     * 删除redis中的登录用户信息
+     *
+     * @param token
+     * @throws Exception
+     */
+    void deleteLoginVo(String token);
+
+    /**
+     * 刷新token
+     *
+     * @throws Exception
+     */
+    void refreshToken();
+
+    /**
+     * 通过用户token删除当前用户之前的所有的redis登录信息
+     *
+     * @param token
+     * @throws Exception
+     */
+    void deleteLoginInfoByToken(String token);
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/service/LoginService.java b/src/main/java/io/geekidea/boot/auth/service/LoginService.java
new file mode 100644
index 00000000..23ac6879
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/service/LoginService.java
@@ -0,0 +1,51 @@
+package io.geekidea.boot.auth.service;
+
+import io.geekidea.boot.auth.dto.LoginDto;
+import io.geekidea.boot.auth.vo.LoginTokenVo;
+import io.geekidea.boot.auth.vo.LoginVo;
+import io.geekidea.boot.system.entity.SysUser;
+
+import java.util.Date;
+
+/**
+ * @author geekidea
+ * @date 2022/7/5
+ **/
+public interface LoginService {
+
+    /**
+     * 登录
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    LoginTokenVo login(LoginDto dto);
+
+    /**
+     * 处理登录用户信息
+     *
+     * @param sysUser
+     * @param token
+     * @param loginTime
+     * @return
+     * @throws Exception
+     */
+    LoginVo refreshLoginInfo(SysUser sysUser, String token, Date loginTime);
+
+    /**
+     * 获取登录用户信息
+     *
+     * @return
+     * @throws Exception
+     */
+    LoginVo getLoginUserInfo();
+
+    /**
+     * 登出
+     *
+     * @throws Exception
+     */
+    void logout();
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/service/impl/AppLoginRedisServiceImpl.java b/src/main/java/io/geekidea/boot/auth/service/impl/AppLoginRedisServiceImpl.java
new file mode 100644
index 00000000..0829a17c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/service/impl/AppLoginRedisServiceImpl.java
@@ -0,0 +1,113 @@
+package io.geekidea.boot.auth.service.impl;
+
+import io.geekidea.boot.auth.service.AppLoginRedisService;
+import io.geekidea.boot.auth.util.TokenUtil;
+import io.geekidea.boot.auth.vo.AppLoginVo;
+import io.geekidea.boot.common.constant.RedisKey;
+import io.geekidea.boot.config.properties.LoginAppProperties;
+import io.geekidea.boot.framework.exception.LoginException;
+import io.geekidea.boot.framework.exception.LoginTokenException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author geekidea
+ * @date 2022/7/12
+ **/
+@Slf4j
+@Service
+public class AppLoginRedisServiceImpl implements AppLoginRedisService {
+
+    private static final TimeUnit TOKEN_TIME_UNIT = TimeUnit.DAYS;
+
+    @Autowired
+    private LoginAppProperties loginAppProperties;
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    private Integer tokenExpireDays;
+
+    @PostConstruct
+    public void init() {
+        log.info("loginAppProperties = " + loginAppProperties);
+        tokenExpireDays = loginAppProperties.getTokenExpireDays();
+    }
+
+    @Override
+    public String getLoginRedisKey(String token) {
+        String loginRedisKey = String.format(RedisKey.LOGIN_TOKEN, token);
+        return loginRedisKey;
+    }
+
+    @Override
+    public void setLoginVo(String token, AppLoginVo appLoginVo) {
+        if (appLoginVo == null) {
+            throw new LoginException("登录用户信息不能为空");
+        }
+        if (loginAppProperties.isSingleLogin()) {
+            // 单点登录，则删除之前该用户的key
+            deleteLoginInfoByToken(token);
+        }
+        // 用户信息
+        String loginTokenRedisKey = getLoginRedisKey(token);
+        redisTemplate.opsForValue().set(loginTokenRedisKey, appLoginVo, tokenExpireDays, TOKEN_TIME_UNIT);
+    }
+
+    @Override
+    public AppLoginVo getLoginVo(String token) {
+        if (StringUtils.isBlank(token)) {
+            throw new LoginTokenException("token不能为空");
+        }
+        String loginRedisKey = getLoginRedisKey(token);
+        AppLoginVo appLoginVo = (AppLoginVo) redisTemplate.opsForValue().get(loginRedisKey);
+        return appLoginVo;
+    }
+
+    @Override
+    public void deleteLoginVo(String token) {
+        if (StringUtils.isBlank(token)) {
+            throw new LoginTokenException("token不能为空");
+        }
+        String loginTokenRedisKey = getLoginRedisKey(token);
+        redisTemplate.delete(loginTokenRedisKey);
+    }
+
+    @Override
+    public void refreshToken() {
+        // 刷新token
+        String token = TokenUtil.getToken();
+        if (StringUtils.isBlank(token)) {
+            return;
+        }
+        // 刷新key的过期时间
+        String loginTokenRedisKey = getLoginRedisKey(token);
+        redisTemplate.expire(loginTokenRedisKey, tokenExpireDays, TOKEN_TIME_UNIT);
+    }
+
+    @Override
+    public void deleteLoginInfoByToken(String token) {
+        log.info("清除用户的所有redis登录信息：" + token);
+        if (StringUtils.isBlank(token)) {
+            throw new LoginTokenException("token不能为空");
+        }
+        int lastIndexOf = token.lastIndexOf(".");
+        String userTokenPrefix = token.substring(0, lastIndexOf + 1);
+        // 删除之前该用户的key
+        String userTokenRedisPrefix = userTokenPrefix + "*";
+        String formatRedisTokenPrefix = String.format(RedisKey.LOGIN_TOKEN, userTokenRedisPrefix);
+        Set keys = redisTemplate.keys(formatRedisTokenPrefix);
+        if (CollectionUtils.isNotEmpty(keys)) {
+            redisTemplate.delete(keys);
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/service/impl/AppLoginServiceImpl.java b/src/main/java/io/geekidea/boot/auth/service/impl/AppLoginServiceImpl.java
new file mode 100644
index 00000000..ff23074b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/service/impl/AppLoginServiceImpl.java
@@ -0,0 +1,183 @@
+package io.geekidea.boot.auth.service.impl;
+
+import io.geekidea.boot.auth.dto.AppAccountLoginDto;
+import io.geekidea.boot.auth.dto.AppLoginDto;
+import io.geekidea.boot.auth.service.AppLoginRedisService;
+import io.geekidea.boot.auth.service.AppLoginService;
+import io.geekidea.boot.auth.util.AppLoginUtil;
+import io.geekidea.boot.auth.util.TokenUtil;
+import io.geekidea.boot.auth.vo.AppLoginVo;
+import io.geekidea.boot.auth.vo.LoginTokenVo;
+import io.geekidea.boot.common.constant.LoginConstant;
+import io.geekidea.boot.common.enums.SystemType;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.framework.exception.LoginException;
+import io.geekidea.boot.user.entity.User;
+import io.geekidea.boot.user.entity.UserRole;
+import io.geekidea.boot.user.service.UserRoleService;
+import io.geekidea.boot.user.service.UserService;
+import io.geekidea.boot.util.IpRegionUtil;
+import io.geekidea.boot.util.IpUtil;
+import io.geekidea.boot.util.PasswordUtil;
+import io.geekidea.boot.util.api.WxMpApi;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+
+/**
+ * @author geekidea
+ * @date 2022/7/12
+ **/
+@Slf4j
+@Service
+public class AppLoginServiceImpl implements AppLoginService {
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private AppLoginRedisService appLoginRedisService;
+
+    @Autowired
+    private UserRoleService userRoleService;
+
+    @Override
+    public LoginTokenVo login(AppLoginDto dto) {
+        String code = dto.getCode();
+        // 获取微信openid
+        String openid = WxMpApi.getOpenid(code);
+        // 根据openid获取用户信息
+        User user = userService.getUserByOpenid(openid);
+        // 判断是否存在，不存在，则添加
+        if (user == null) {
+            // 注册
+            user = new User();
+            // 用户昵称：用户+8为随机数字
+            user.setNickname("用户" + RandomStringUtils.randomNumeric(8));
+            // 设置微信小程序openid
+            user.setOpenid(openid);
+            // 设置注册时间
+            user.setRegisterTime(new Date());
+            String requestIp = IpUtil.getRequestIp();
+            String ipAreaDesc = IpRegionUtil.getIpAreaDesc(requestIp);
+            // 设置注册IP
+            user.setRegisterIp(requestIp);
+            // 设置注册IP区域
+            user.setRegisterIpArea(ipAreaDesc);
+            // 设置默认用户为普通角色
+            user.setUserRoleId(LoginConstant.APP_NORMAL_USER_ROLE);
+            boolean flag = userService.save(user);
+            if (!flag) {
+                throw new BusinessException("注册异常");
+            }
+            // 获取用户ID
+            Long userId = user.getId();
+            // 根据用户ID获取用户下信息
+            user = userService.getById(userId);
+        }
+        return login(user);
+    }
+
+    @Override
+    public LoginTokenVo accountLogin(AppAccountLoginDto dto) {
+        String username = dto.getUsername();
+        User user = userService.getUserByUsername(username);
+        // 校验密码
+        String dbPassword = user.getPassword();
+        String dbSalt = user.getSalt();
+        String password = dto.getPassword();
+        String encryptPassword = PasswordUtil.encrypt(password, dbSalt);
+        if (!encryptPassword.equals(dbPassword)) {
+            throw new BusinessException("账号密码错误");
+        }
+        return login(user);
+    }
+
+    @Override
+    public LoginTokenVo login(User user) {
+        if (user == null) {
+            throw new LoginException("用户信息不存在");
+        }
+        Long userId = user.getId();
+        // 生成token
+        String token = TokenUtil.generateAppToken(userId);
+        // 最后一次登录时间
+        Date lastLoginTime = new Date();
+        // 刷新登录信息
+        refreshLoginInfo(user, token, lastLoginTime);
+        // 设置登录信息
+        String requestIp = IpUtil.getRequestIp();
+        String ipAreaDesc = IpRegionUtil.getIpAreaDesc(requestIp);
+        // 设置最后登录时间
+        user.setLastLoginTime(lastLoginTime);
+        // 设置最后登录IP
+        user.setLastLoginIp(requestIp);
+        // 设置最后登录IP区域
+        user.setLastLoginIpArea(ipAreaDesc);
+        userService.updateById(user);
+
+        // TODO 添加用户登录日志
+
+        // 返回token
+        LoginTokenVo loginTokenVo = new LoginTokenVo();
+        loginTokenVo.setToken(token);
+        return loginTokenVo;
+    }
+
+    @Override
+    public AppLoginVo refreshLoginInfo(User user, String token, Date lastLoginTime) {
+        Long userId = user.getId();
+        // 校验用户状态
+        Boolean status = user.getStatus();
+        if (status == false) {
+            throw new LoginException("用户已禁用");
+        }
+        // 设置登录用户对象
+        AppLoginVo appLoginVo = new AppLoginVo();
+        BeanUtils.copyProperties(user, appLoginVo);
+        appLoginVo.setUserId(userId);
+        appLoginVo.setLastLoginTime(lastLoginTime);
+        // 系统类型 1：管理后台，2：用户端
+        appLoginVo.setSystemType(SystemType.APP.getCode());
+        Long userRoleId = user.getUserRoleId();
+        appLoginVo.setUserRoleId(userRoleId);
+        if (userRoleId != null) {
+            UserRole userRole = userRoleService.getById(userRoleId);
+            if (userRole != null) {
+                appLoginVo.setUserRoleCode(userRole.getCode());
+                appLoginVo.setUserRoleName(userRole.getName());
+            }
+
+        }
+        // 保存登录信息到redis中
+        appLoginRedisService.setLoginVo(token, appLoginVo);
+        return appLoginVo;
+    }
+
+    @Override
+    public AppLoginVo getLoginUserInfo() {
+        AppLoginVo appLoginVo = AppLoginUtil.getLoginVo();
+        if (appLoginVo == null) {
+            throw new LoginException("请先登录");
+        }
+        Long userId = appLoginVo.getUserId();
+        User user = userService.getById(userId);
+        // 刷新用户登录信息
+        String token = TokenUtil.getToken();
+        Date lastLoginTime = appLoginVo.getLastLoginTime();
+        appLoginVo = refreshLoginInfo(user, token, lastLoginTime);
+        return appLoginVo;
+    }
+
+    @Override
+    public void logout() {
+        // 获取token
+        String token = TokenUtil.getToken();
+        // 删除缓存
+        appLoginRedisService.deleteLoginVo(token);
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/auth/service/impl/LoginRedisServiceImpl.java b/src/main/java/io/geekidea/boot/auth/service/impl/LoginRedisServiceImpl.java
new file mode 100644
index 00000000..b1682f17
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/service/impl/LoginRedisServiceImpl.java
@@ -0,0 +1,113 @@
+package io.geekidea.boot.auth.service.impl;
+
+import io.geekidea.boot.auth.service.LoginRedisService;
+import io.geekidea.boot.auth.util.TokenUtil;
+import io.geekidea.boot.auth.vo.LoginVo;
+import io.geekidea.boot.common.constant.RedisKey;
+import io.geekidea.boot.config.properties.LoginAdminProperties;
+import io.geekidea.boot.framework.exception.LoginException;
+import io.geekidea.boot.framework.exception.LoginTokenException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author geekidea
+ * @date 2022/7/12
+ **/
+@Slf4j
+@Service
+public class LoginRedisServiceImpl implements LoginRedisService {
+
+    private static final TimeUnit TOKEN_TIME_UNIT = TimeUnit.MINUTES;
+
+    @Autowired
+    private LoginAdminProperties loginAdminProperties;
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    private Integer tokenExpireMinutes;
+
+    @PostConstruct
+    public void init() {
+        log.info("loginAdminProperties = " + loginAdminProperties);
+        tokenExpireMinutes = loginAdminProperties.getTokenExpireMinutes();
+    }
+
+    @Override
+    public String getLoginRedisKey(String token) {
+        String loginRedisKey = String.format(RedisKey.LOGIN_TOKEN, token);
+        return loginRedisKey;
+    }
+
+    @Override
+    public void setLoginVo(String token, LoginVo loginVo) {
+        if (loginVo == null) {
+            throw new LoginException("登录用户信息不能为空");
+        }
+        if (loginAdminProperties.isSingleLogin()) {
+            // 单点登录，则删除之前该用户的key
+            deleteLoginInfoByToken(token);
+        }
+        // 用户信息
+        String loginTokenRedisKey = getLoginRedisKey(token);
+        redisTemplate.opsForValue().set(loginTokenRedisKey, loginVo, tokenExpireMinutes, TOKEN_TIME_UNIT);
+    }
+
+    @Override
+    public LoginVo getLoginVo(String token) {
+        if (StringUtils.isBlank(token)) {
+            throw new LoginTokenException("token不能为空");
+        }
+        String loginRedisKey = getLoginRedisKey(token);
+        LoginVo loginVo = (LoginVo) redisTemplate.opsForValue().get(loginRedisKey);
+        return loginVo;
+    }
+
+    @Override
+    public void deleteLoginVo(String token) {
+        if (StringUtils.isBlank(token)) {
+            throw new LoginTokenException("token不能为空");
+        }
+        String loginTokenRedisKey = getLoginRedisKey(token);
+        redisTemplate.delete(loginTokenRedisKey);
+    }
+
+    @Override
+    public void refreshToken() {
+        // 刷新token
+        String token = TokenUtil.getToken();
+        if (StringUtils.isBlank(token)) {
+            return;
+        }
+        // 刷新key的过期时间
+        String loginTokenRedisKey = getLoginRedisKey(token);
+        redisTemplate.expire(loginTokenRedisKey, tokenExpireMinutes, TOKEN_TIME_UNIT);
+    }
+
+    @Override
+    public void deleteLoginInfoByToken(String token) {
+        log.info("清除用户的所有redis登录信息：" + token);
+        if (StringUtils.isBlank(token)) {
+            throw new LoginTokenException("token不能为空");
+        }
+        int lastIndexOf = token.lastIndexOf(".");
+        String userTokenPrefix = token.substring(0, lastIndexOf + 1);
+        // 删除之前该用户的key
+        String userTokenRedisPrefix = userTokenPrefix + "*";
+        String formatRedisTokenPrefix = String.format(RedisKey.LOGIN_TOKEN, userTokenRedisPrefix);
+        Set keys = redisTemplate.keys(formatRedisTokenPrefix);
+        if (CollectionUtils.isNotEmpty(keys)) {
+            redisTemplate.delete(keys);
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/service/impl/LoginServiceImpl.java b/src/main/java/io/geekidea/boot/auth/service/impl/LoginServiceImpl.java
new file mode 100644
index 00000000..a4693984
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/service/impl/LoginServiceImpl.java
@@ -0,0 +1,139 @@
+package io.geekidea.boot.auth.service.impl;
+
+import io.geekidea.boot.auth.dto.LoginDto;
+import io.geekidea.boot.auth.service.LoginRedisService;
+import io.geekidea.boot.auth.service.LoginService;
+import io.geekidea.boot.auth.util.LoginUtil;
+import io.geekidea.boot.auth.util.TokenUtil;
+import io.geekidea.boot.auth.vo.LoginTokenVo;
+import io.geekidea.boot.auth.vo.LoginVo;
+import io.geekidea.boot.common.enums.SystemType;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.framework.exception.LoginException;
+import io.geekidea.boot.system.entity.SysRole;
+import io.geekidea.boot.system.entity.SysUser;
+import io.geekidea.boot.system.mapper.SysMenuMapper;
+import io.geekidea.boot.system.mapper.SysRoleMapper;
+import io.geekidea.boot.system.mapper.SysUserMapper;
+import io.geekidea.boot.util.PasswordUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2022/7/12
+ **/
+@Slf4j
+@Service
+public class LoginServiceImpl implements LoginService {
+
+    @Autowired
+    private SysUserMapper sysUserMapper;
+
+    @Autowired
+    private SysRoleMapper sysRoleMapper;
+
+    @Autowired
+    private SysMenuMapper sysMenuMapper;
+
+    @Autowired
+    private LoginRedisService loginRedisService;
+
+
+    @Override
+    public LoginTokenVo login(LoginDto dto) {
+        String username = dto.getUsername();
+        SysUser sysUser = sysUserMapper.getSysUserByUsername(username);
+        if (sysUser == null) {
+            throw new BusinessException("用户信息不存在");
+        }
+        // 用户ID
+        Long userId = sysUser.getId();
+        // 校验密码
+        String dbPassword = sysUser.getPassword();
+        String dbSalt = sysUser.getSalt();
+        String password = dto.getPassword();
+        String encryptPassword = PasswordUtil.encrypt(password, dbSalt);
+        if (!encryptPassword.equals(dbPassword)) {
+            throw new BusinessException("账号密码错误");
+        }
+        // 生成token
+        String token = TokenUtil.generateAdminToken(userId);
+        // 刷新登录信息
+        refreshLoginInfo(sysUser, token, new Date());
+        // 返回token
+        LoginTokenVo loginTokenVo = new LoginTokenVo();
+        loginTokenVo.setToken(token);
+        return loginTokenVo;
+    }
+
+    @Override
+    public LoginVo refreshLoginInfo(SysUser sysUser, String token, Date loginTime) {
+        // 用户ID
+        Long userId = sysUser.getId();
+        // 校验用户状态
+        Boolean status = sysUser.getStatus();
+        if (status == false) {
+            throw new BusinessException("用户已禁用");
+        }
+        // 查询用户角色
+        Long roleId = sysUser.getRoleId();
+        SysRole sysRole = sysRoleMapper.selectById(roleId);
+        if (sysRole == null) {
+            throw new BusinessException("该用户不存在可用的角色");
+        }
+        // 设置登录用户对象
+        LoginVo loginVo = new LoginVo();
+        BeanUtils.copyProperties(sysUser, loginVo);
+        loginVo.setUserId(userId);
+        loginVo.setLoginTime(loginTime);
+        loginVo.setAdmin(sysUser.getIsAdmin());
+        loginVo.setRoleCode(sysRole.getCode());
+        loginVo.setRoleName(sysRole.getName());
+        // 系统类型 1：管理后台，2：用户端
+        loginVo.setSystemType(SystemType.ADMIN.getCode());
+        // 获取登录用户的权限编码
+        List<String> permissions = sysMenuMapper.getPermissionCodesByUserId(userId);
+        loginVo.setPermissions(permissions);
+        // 保存登录信息到redis中
+        loginRedisService.setLoginVo(token, loginVo);
+        return loginVo;
+    }
+
+    @Override
+    public LoginVo getLoginUserInfo() {
+        LoginVo loginVo = LoginUtil.getLoginVo();
+        if (loginVo == null) {
+            throw new LoginException("请先登录");
+        }
+        // 根据用户ID获取用户信息
+        Long userId = loginVo.getUserId();
+        // 获取用户登录时间
+        Date loginTime = loginVo.getLoginTime();
+        SysUser sysUser = sysUserMapper.selectById(userId);
+        if (sysUser == null) {
+            throw new BusinessException("用户信息不存在");
+        }
+        // 刷新登录信息
+        String token = TokenUtil.getToken();
+        loginVo = refreshLoginInfo(sysUser, token, loginTime);
+        return loginVo;
+    }
+
+    @Override
+    public void logout() {
+        try {
+            // 获取token
+            String token = TokenUtil.getToken();
+            // 删除缓存
+            loginRedisService.deleteLoginVo(token);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/auth/util/AppLoginUtil.java b/src/main/java/io/geekidea/boot/auth/util/AppLoginUtil.java
new file mode 100644
index 00000000..9dfc353c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/util/AppLoginUtil.java
@@ -0,0 +1,112 @@
+package io.geekidea.boot.auth.util;
+
+import io.geekidea.boot.auth.cache.AppLoginCache;
+import io.geekidea.boot.auth.service.AppLoginRedisService;
+import io.geekidea.boot.auth.vo.AppLoginVo;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Component
+public class AppLoginUtil {
+
+    private static AppLoginRedisService appLoginRedisService;
+
+    public AppLoginUtil(AppLoginRedisService appLoginRedisService) {
+        AppLoginUtil.appLoginRedisService = appLoginRedisService;
+    }
+
+    /**
+     * 根据token从redis中获取登录用户信息
+     *
+     * @param token
+     * @return
+     * @
+     */
+    public static AppLoginVo getLoginVo(String token) {
+        if (StringUtils.isBlank(token)) {
+            return null;
+        }
+        AppLoginVo appLoginVo = appLoginRedisService.getLoginVo(token);
+        return appLoginVo;
+    }
+
+    /**
+     * 从当前线程中获取登录用户信息
+     *
+     * @return
+     */
+    public static AppLoginVo getLoginVo() {
+        return AppLoginCache.get();
+    }
+
+    /**
+     * 获取登录用户ID
+     *
+     * @return
+     */
+    public static Long getUserId() {
+        AppLoginVo appLoginVo = getLoginVo();
+        if (appLoginVo != null) {
+            return appLoginVo.getUserId();
+        }
+        return null;
+    }
+
+    /**
+     * 获取登录用户ID字符串
+     *
+     * @return
+     * @
+     */
+    public static String getUserIdString() {
+        Long userId = getUserId();
+        if (userId == null) {
+            return null;
+        }
+        return userId.toString();
+    }
+
+    /**
+     * 获取登录用户名
+     *
+     * @return
+     */
+    public static String getUsername() {
+        AppLoginVo appLoginVo = getLoginVo();
+        if (appLoginVo != null) {
+            return appLoginVo.getUsername();
+        }
+        return null;
+    }
+
+    /**
+     * 获取用户角色ID
+     *
+     * @return
+     */
+    public static Long getUserRoleId() {
+        AppLoginVo appLoginVo = getLoginVo();
+        if (appLoginVo != null) {
+            return appLoginVo.getUserRoleId();
+        }
+        return null;
+    }
+
+    /**
+     * 获取用户角色编码
+     *
+     * @return
+     */
+    public static String getUserRoleCode() {
+        AppLoginVo appLoginVo = getLoginVo();
+        if (appLoginVo != null) {
+            return appLoginVo.getUserRoleCode();
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/util/CommonLoginUtil.java b/src/main/java/io/geekidea/boot/auth/util/CommonLoginUtil.java
new file mode 100644
index 00000000..0c79d175
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/util/CommonLoginUtil.java
@@ -0,0 +1,75 @@
+package io.geekidea.boot.auth.util;
+
+import io.geekidea.boot.common.enums.SystemType;
+import io.geekidea.boot.util.SystemTypeUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author geekidea
+ * @date 2023/11/24
+ **/
+@Component
+public class CommonLoginUtil {
+
+    /**
+     * 根据token获取用户ID
+     *
+     * @param token
+     * @return
+     * @
+     */
+    public static Long getUserId(String token) {
+        if (StringUtils.isBlank(token)) {
+            return null;
+        }
+        SystemType systemType = SystemTypeUtil.getSystemTypeByToken(token);
+        return getUserId(systemType);
+    }
+
+    /**
+     * 获取用户ID
+     *
+     * @return
+     * @
+     */
+    public static Long getUserId() {
+        String token = TokenUtil.getToken();
+        return getUserId(token);
+    }
+
+    /**
+     * 获取登录用户ID字符串
+     *
+     * @return
+     * @
+     */
+    public static String getUserIdString() {
+        Long userId = getUserId();
+        if (userId == null) {
+            return null;
+        }
+        return userId.toString();
+    }
+
+    /**
+     * 根据系统类型获取用户ID
+     *
+     * @param systemType
+     * @return
+     * @
+     */
+    public static Long getUserId(SystemType systemType) {
+        try {
+            if (SystemType.ADMIN == systemType) {
+                return LoginUtil.getUserId();
+            } else if (SystemType.APP == systemType) {
+                return AppLoginUtil.getUserId();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/util/LoginUtil.java b/src/main/java/io/geekidea/boot/auth/util/LoginUtil.java
new file mode 100644
index 00000000..86c99438
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/util/LoginUtil.java
@@ -0,0 +1,150 @@
+package io.geekidea.boot.auth.util;
+
+import io.geekidea.boot.auth.cache.LoginCache;
+import io.geekidea.boot.auth.service.LoginRedisService;
+import io.geekidea.boot.auth.vo.LoginVo;
+import io.geekidea.boot.framework.exception.BusinessException;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Component
+public class LoginUtil {
+
+    private static LoginRedisService loginRedisService;
+
+    public LoginUtil(LoginRedisService loginRedisService) {
+        LoginUtil.loginRedisService = loginRedisService;
+    }
+
+    /**
+     * 根据token从redis中获取登录用户信息
+     *
+     * @param token
+     * @return
+     * @
+     */
+    public static LoginVo getLoginVo(String token) {
+        if (StringUtils.isBlank(token)) {
+            return null;
+        }
+        LoginVo loginVo = loginRedisService.getLoginVo(token);
+        return loginVo;
+    }
+
+    /**
+     * 从当前线程中获取登录用户信息
+     *
+     * @return
+     */
+    public static LoginVo getLoginVo() {
+        return LoginCache.get();
+    }
+
+    /**
+     * 获取登录信息
+     *
+     * @return
+     */
+    public static List<String> getPermissions() {
+        LoginVo loginVo = getLoginVo();
+        if (loginVo != null) {
+            return loginVo.getPermissions();
+        }
+        return null;
+    }
+
+    /**
+     * 获取登录用户ID
+     *
+     * @return
+     */
+    public static Long getUserId() {
+        LoginVo loginVo = getLoginVo();
+        if (loginVo != null) {
+            Long userId = loginVo.getUserId();
+            return userId;
+        }
+        return null;
+    }
+
+    /**
+     * 获取登录用户ID字符串
+     *
+     * @return
+     * @
+     */
+    public static String getUserIdString() {
+        Long userId = getUserId();
+        if (userId == null) {
+            return null;
+        }
+        return userId.toString();
+    }
+
+    /**
+     * 获取登录用户名
+     *
+     * @return
+     */
+    public static String getUsername() {
+        LoginVo loginVo = getLoginVo();
+        if (loginVo != null) {
+            return loginVo.getUsername();
+        }
+        return null;
+    }
+
+    /**
+     * 获取登录角色ID
+     *
+     * @return
+     */
+    public static Long getRoleId() {
+        LoginVo loginVo = getLoginVo();
+        if (loginVo != null) {
+            return loginVo.getRoleId();
+        }
+        return null;
+    }
+
+    /**
+     * 判断是否是管理员
+     *
+     * @return
+     * @
+     */
+    public static boolean isAdmin() {
+        LoginVo loginVo = getLoginVo();
+        if (loginVo != null) {
+            return loginVo.isAdmin();
+        }
+        return false;
+    }
+
+    /**
+     * 判断不是管理员
+     *
+     * @return
+     * @
+     */
+    public static boolean isNotAdmin() {
+        return !isAdmin();
+    }
+
+    /**
+     * 检查是否是管理员
+     */
+    public static void checkAdmin() {
+        boolean admin = isAdmin();
+        if (!admin) {
+            throw new BusinessException("不是管理员，无权限");
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/util/TokenUtil.java b/src/main/java/io/geekidea/boot/auth/util/TokenUtil.java
new file mode 100644
index 00000000..3c992c08
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/util/TokenUtil.java
@@ -0,0 +1,190 @@
+package io.geekidea.boot.auth.util;
+
+import io.geekidea.boot.auth.cache.TokenCache;
+import io.geekidea.boot.common.constant.LoginConstant;
+import io.geekidea.boot.common.enums.SystemType;
+import io.geekidea.boot.framework.exception.LoginTokenException;
+import io.geekidea.boot.util.CookieUtil;
+import io.geekidea.boot.util.HttpServletRequestUtil;
+import io.geekidea.boot.util.SystemTypeUtil;
+import io.geekidea.boot.util.UUIDUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Token工具类
+ *
+ * @author geekidea
+ * @date 2022-1-05
+ **/
+@Slf4j
+@Component
+public class TokenUtil {
+
+    /**
+     * 生成Admin后台Token
+     *
+     * @param userId
+     * @return
+     * @throws Exception
+     */
+    public static String generateAdminToken(Long userId) {
+        String userMd5 = DigestUtils.md5Hex(userId.toString());
+        String adminToken = LoginConstant.ADMIN_TOKEN_PREFIX + userMd5 + "." + UUIDUtil.getUuid();
+        return adminToken;
+    }
+
+    /**
+     * 生成用户端Token
+     *
+     * @param userId
+     * @return
+     * @throws Exception
+     */
+    public static String generateAppToken(Long userId) {
+        String userMd5 = DigestUtils.md5Hex(userId.toString());
+        String appToken = LoginConstant.APP_TOKEN_PREFIX + userMd5 + "." + UUIDUtil.getUuid();
+        return appToken;
+    }
+
+    /**
+     * 获取短的ID
+     *
+     * @param userId
+     * @return
+     * @throws Exception
+     */
+    public static String getShortId(Long userId) {
+        // 将数字转换成数字加字母变为更短的字符串
+        // 36 表示基数(10 位数字 + 26 个字符)
+        String string = Long.toString(userId, 36);
+        return string;
+    }
+
+    /**
+     * 解析短的ID
+     *
+     * @param shorUserId
+     * @return
+     * @throws Exception
+     */
+    public static Long parseShortId(String shorUserId) {
+        long userId = Long.parseLong(shorUserId, 36);
+        return userId;
+    }
+
+    /**
+     * 获取token
+     *
+     * @return
+     */
+    public static String getToken() {
+        // 从当前线程获取
+        return TokenCache.get();
+    }
+
+    /**
+     * 从请求头或者请求参数中获取token
+     *
+     * @param request
+     * @return
+     */
+    public static String getToken(HttpServletRequest request) {
+        if (request == null) {
+            throw new IllegalArgumentException("request不能为空");
+        }
+        // 从请求头中获取token
+        String token = request.getHeader(LoginConstant.TOKEN_NAME);
+        if (StringUtils.isBlank(token)) {
+            // 从请求参数中获取token
+            token = request.getParameter(LoginConstant.TOKEN_NAME);
+        }
+        String servletPath = request.getServletPath();
+        SystemType systemType = SystemTypeUtil.getSystemTypeByPath(servletPath);
+        if (StringUtils.isBlank(token)) {
+            // 从cookie中获取token
+            token = getTokenByCookie(request, systemType);
+            if (StringUtils.isNotBlank(token) && !token.startsWith(LoginConstant.TOKEN_PREFIX)) {
+                token = null;
+            }
+        }
+        if (StringUtils.isBlank(token)) {
+            return null;
+        }
+        // 校验token
+        if (!token.startsWith(LoginConstant.TOKEN_PREFIX)) {
+            log.error("token错误:" + token);
+            throw new LoginTokenException("token错误");
+        }
+        if (SystemType.ADMIN == systemType) {
+            checkAdminToken(token);
+        } else if (SystemType.APP == systemType) {
+            checkAppToken(token);
+        }
+        return token;
+    }
+
+    /**
+     * 从Cookie中获取token
+     *
+     * @param request
+     * @return
+     */
+    public static String getTokenByCookie(HttpServletRequest request, SystemType systemType) {
+        Cookie[] cookies = request.getCookies();
+        if (ArrayUtils.isEmpty(cookies)) {
+            return null;
+        }
+        if (SystemType.ADMIN == systemType) {
+            // 管理系统token的cookie可以通过接口文档传递或者浏览器页面传递
+            return CookieUtil.getCookieValueByName(cookies, LoginConstant.ADMIN_COOKIE_TOKEN_NAME);
+        } else if (SystemType.APP == systemType) {
+            // 判断是否是接口文档请求，是则从cookie中获取，否则不获取，app接口只能通过接口文档传递token的cookie
+            if (HttpServletRequestUtil.isDocRequest()) {
+                return CookieUtil.getCookieValueByName(cookies, LoginConstant.APP_COOKIE_TOKEN_NAME);
+            }
+            return null;
+        } else {
+            String cookieValue = CookieUtil.getCookieValueByName(cookies, LoginConstant.ADMIN_COOKIE_TOKEN_NAME);
+            if (StringUtils.isBlank(cookieValue)) {
+                if (HttpServletRequestUtil.isDocRequest()) {
+                    cookieValue = CookieUtil.getCookieValueByName(cookies, LoginConstant.APP_COOKIE_TOKEN_NAME);
+                }
+            }
+            return cookieValue;
+        }
+    }
+
+    /**
+     * 校验是否是admin的token
+     *
+     * @param token
+     * @throws Exception
+     */
+    public static void checkAdminToken(String token) {
+        SystemType systemType = SystemTypeUtil.getSystemTypeByToken(token);
+        if (SystemType.ADMIN != systemType) {
+            throw new LoginTokenException("非管理后台token");
+        }
+    }
+
+    /**
+     * 校验是否是app的token
+     *
+     * @param token
+     * @throws Exception
+     */
+    public static void checkAppToken(String token) {
+        SystemType systemType = SystemTypeUtil.getSystemTypeByToken(token);
+        if (SystemType.APP != systemType) {
+            throw new LoginTokenException("非移动端token");
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/vo/AppLoginVo.java b/src/main/java/io/geekidea/boot/auth/vo/AppLoginVo.java
new file mode 100644
index 00000000..721567ca
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/vo/AppLoginVo.java
@@ -0,0 +1,58 @@
+package io.geekidea.boot.auth.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author geekidea
+ * @date 2023/11/26
+ **/
+@Data
+@Schema(description = "登录AppVo")
+public class AppLoginVo implements Serializable {
+
+    private static final long serialVersionUID = 3338162302996457084L;
+
+    @Schema(description = "用户Id")
+    private Long userId;
+
+    @Schema(description = "账号")
+    private String username;
+
+    @Schema(description = "昵称")
+    private String nickname;
+
+    @Schema(description = "微信openid")
+    private String openid;
+
+    @Schema(description = "手机号码")
+    private String phone;
+
+    @Schema(description = "头像")
+    private String head;
+
+    @Schema(description = "用户角色ID")
+    private Long userRoleId;
+
+    @Schema(description = "用户角色编码")
+    private String userRoleCode;
+
+    @Schema(description = "用户角色名称")
+    private String userRoleName;
+
+    @Schema(description = "状态 1：正常，0：禁用")
+    private Boolean status;
+
+    @Schema(description = "最后登录时间")
+    private Date lastLoginTime;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "系统类型 1：管理后台，2：用户端")
+    private Integer systemType;
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/vo/LoginTokenVo.java b/src/main/java/io/geekidea/boot/auth/vo/LoginTokenVo.java
new file mode 100644
index 00000000..69325122
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/vo/LoginTokenVo.java
@@ -0,0 +1,17 @@
+package io.geekidea.boot.auth.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Data
+@Schema(description = "LoginVo")
+public class LoginTokenVo {
+
+    @Schema(description = "登录令牌")
+    private String token;
+
+}
diff --git a/src/main/java/io/geekidea/boot/auth/vo/LoginVo.java b/src/main/java/io/geekidea/boot/auth/vo/LoginVo.java
new file mode 100644
index 00000000..29db4aa1
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/auth/vo/LoginVo.java
@@ -0,0 +1,62 @@
+package io.geekidea.boot.auth.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Data
+@Schema(description = "登录Vo")
+public class LoginVo implements Serializable {
+
+    private static final long serialVersionUID = 3338162302996457084L;
+
+    @Schema(description = "用户Id")
+    private Long userId;
+
+    @Schema(description = "用户名")
+    private String username;
+
+    @Schema(description = "用户昵称")
+    private String nickname;
+
+    @Schema(description = "手机号码")
+    private String phone;
+
+    @Schema(description = "电子邮件")
+    private String email;
+
+    @Schema(description = "头像")
+    private String head;
+
+    @Schema(description = "是否是管理员")
+    private boolean admin;
+
+    @Schema(description = "角色ID")
+    private Long roleId;
+
+    @Schema(description = "角色编码")
+    private String roleCode;
+
+    @Schema(description = "角色名称")
+    private String roleName;
+
+    @Schema(description = "登录时间")
+    private Date loginTime;
+
+    @Schema(description = "系统类型 1：管理后台，2：用户端")
+    private Integer systemType;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "权限code集合")
+    private List<String> permissions;
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/constant/AopConstant.java b/src/main/java/io/geekidea/boot/common/constant/AopConstant.java
new file mode 100644
index 00000000..3df39c88
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/constant/AopConstant.java
@@ -0,0 +1,29 @@
+package io.geekidea.boot.common.constant;
+
+/**
+ * @author geekidea
+ * @date 2022/7/3
+ **/
+public interface AopConstant {
+
+    /**
+     * 项目下所有controller切点
+     */
+    String PROJECT_POINTCUT = "execution(public * " + CommonConstant.PACKAGE_NAME + "..*.controller..*.*(..))";
+
+    /**
+     * APP切点
+     */
+    String APP_POINTCUT = "execution(public * " + CommonConstant.PACKAGE_NAME + "..*.controller..App*Controller*.*(..))";
+
+    /**
+     * 管理后台切点
+     */
+    String ADMIN_POINTCUT = "execution(public * " + CommonConstant.PACKAGE_NAME + "..*.controller..*.*(..)) && !" + APP_POINTCUT;
+
+    /**
+     * 公共包切点
+     */
+    String COMMON_POINTCUT = "execution(public * " + CommonConstant.COMMON_PACKAGE_NAME + "..*.controller..*.*(..))";
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/constant/CommonConstant.java b/src/main/java/io/geekidea/boot/common/constant/CommonConstant.java
new file mode 100644
index 00000000..9a161be8
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/constant/CommonConstant.java
@@ -0,0 +1,103 @@
+package io.geekidea.boot.common.constant;
+
+/**
+ * 公共常量
+ *
+ * @author geekidea
+ * @date 2018-11-08
+ */
+public interface CommonConstant {
+
+    /**
+     * 项目包名称
+     */
+    String PACKAGE_NAME = "io.geekidea.boot";
+
+    /**
+     * 公共包名称
+     */
+    String COMMON_PACKAGE_NAME = PACKAGE_NAME + ".common";
+
+    /**
+     * 默认页码为1
+     */
+    Integer DEFAULT_PAGE_INDEX = 1;
+
+    /**
+     * 默认页大小为10
+     */
+    Integer DEFAULT_PAGE_SIZE = 10;
+
+    String COMMA = ",";
+
+    /**
+     * 请求的原始字符串
+     */
+    String REQUEST_PARAM_BODY_STRING = "REQUEST_PARAM_BODY_STRING";
+
+    /**
+     * 请求的token
+     */
+    String REQUEST_PARAM_EXCLUDE_PATH = "REQUEST_PARAM_EXCLUDE_PATH";
+
+    /**
+     * 日志链路ID
+     */
+    String TRACE_ID = "traceId";
+
+    /**
+     * 请求IP
+     */
+    String IP = "ip";
+
+    /**
+     * 1000
+     */
+    int ONE_THOUSAND = 1000;
+
+    /**
+     * Knife4j
+     */
+    String KNIFE4J = "Knife4j";
+
+    /**
+     * swaggerUI访问路径
+     */
+    String SWAGGER_UI_PATH = "/swagger-ui/index.html";
+
+    /**
+     * xls后缀
+     */
+    String XLS = "xls";
+
+    /**
+     * xlsx后缀
+     */
+    String XLSX = "xlsx";
+
+    /**
+     * 当前项目目录
+     */
+    String USER_DIR = System.getProperty("user.dir");
+
+    /**
+     * 文件上传目录
+     */
+    String UPLOAD = "upload";
+
+    /**
+     * 正斜杠
+     */
+    String SLASH = "/";
+
+    /**
+     * 反斜杠
+     */
+    String BACKSLASH = "\\";
+
+    /**
+     * 双反斜杠
+     */
+    String DOUBLE_BACKSLASH = "\\\\";
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/constant/LoginConstant.java b/src/main/java/io/geekidea/boot/common/constant/LoginConstant.java
new file mode 100644
index 00000000..888e70bc
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/constant/LoginConstant.java
@@ -0,0 +1,82 @@
+package io.geekidea.boot.common.constant;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2022/7/12
+ **/
+public interface LoginConstant {
+
+    /**
+     * 默认的token名称
+     */
+    String TOKEN_NAME = "Authorization";
+
+    /**
+     * token前缀
+     */
+    String TOKEN_PREFIX = "boot.";
+
+    /**
+     * 管理后台token前缀
+     * boot.admin.
+     */
+    String ADMIN_TOKEN_PREFIX = TOKEN_PREFIX + "admin.";
+
+    /**
+     * 移动端token前缀
+     * boot.app.
+     */
+    String APP_TOKEN_PREFIX = TOKEN_PREFIX + "app.";
+
+    /**
+     * cookie.
+     */
+    String COOKIE_PREFIX = "cookie.";
+
+    /**
+     * 管理后台cookie token名称
+     * boot.admin.cookie.token
+     */
+    String ADMIN_COOKIE_TOKEN_NAME = ADMIN_TOKEN_PREFIX + COOKIE_PREFIX + TOKEN_NAME;
+
+    /**
+     * 移动端cookie token名称
+     * boot.app.cookie.token
+     */
+    String APP_COOKIE_TOKEN_NAME = APP_TOKEN_PREFIX + COOKIE_PREFIX + TOKEN_NAME;
+
+    String ADMIN_URL_PREFIX = "/admin";
+
+    String APP_URL_PREFIX = "/app";
+
+    String COMMON_URL_PREFIX = "/common";
+
+    /**
+     * admin默认的token过期时间 120分钟
+     */
+    Integer ADMIN_TOKEN_EXPIRE_MINUTES = 120;
+
+    /**
+     * app默认的token过期时间 7天
+     */
+    Integer APP_TOKEN_EXPIRE_DAYS = 7;
+
+    /**
+     * 管理员角色编码
+     */
+    String ADMIN_ROLE_CODE = "admin";
+
+    /**
+     * 管理员admin角色ID
+     */
+    List<Long> ADMIN_ROLE_ID_LIST = Arrays.asList(1L);
+
+    /**
+     * APP端普通用户角色
+     */
+    Long APP_NORMAL_USER_ROLE = 1L;
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/constant/RedisKey.java b/src/main/java/io/geekidea/boot/common/constant/RedisKey.java
new file mode 100644
index 00000000..8f9fecd3
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/constant/RedisKey.java
@@ -0,0 +1,14 @@
+package io.geekidea.boot.common.constant;
+
+/**
+ * @author geekidea
+ * @date 2022/7/9
+ **/
+public interface RedisKey {
+
+    /**
+     * 登录token
+     */
+    String LOGIN_TOKEN = "login:token:%s";
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/constant/RequestHeadConstant.java b/src/main/java/io/geekidea/boot/common/constant/RequestHeadConstant.java
new file mode 100644
index 00000000..546a6b43
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/constant/RequestHeadConstant.java
@@ -0,0 +1,21 @@
+package io.geekidea.boot.common.constant;
+
+/**
+ * 请求头常量
+ *
+ * @author geekidea
+ * @date 2023/12/7
+ **/
+public interface RequestHeadConstant {
+
+    /**
+     * 请求源
+     */
+    String REQUEST_ORIGION = "request-origion";
+
+    /**
+     * 来自于那个路径
+     */
+    String REFERER = "referer";
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/constant/SysDictConstant.java b/src/main/java/io/geekidea/boot/common/constant/SysDictConstant.java
new file mode 100644
index 00000000..1e267c8b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/constant/SysDictConstant.java
@@ -0,0 +1,10 @@
+package io.geekidea.boot.common.constant;
+
+/**
+ * @author geekidea
+ * @date 2023/11/25
+ **/
+public interface SysDictConstant {
+
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/constant/SystemConstant.java b/src/main/java/io/geekidea/boot/common/constant/SystemConstant.java
new file mode 100644
index 00000000..41d32bcb
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/constant/SystemConstant.java
@@ -0,0 +1,16 @@
+package io.geekidea.boot.common.constant;
+
+/**
+ * 系统模块常量
+ *
+ * @author geekidea
+ * @date 2022/7/10
+ **/
+public interface SystemConstant {
+
+    /**
+     * 根菜单ID默认为0
+     */
+    Long ROOT_MENU_ID = 0L;
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/constant/UploadConstant.java b/src/main/java/io/geekidea/boot/common/constant/UploadConstant.java
new file mode 100644
index 00000000..c41b5019
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/constant/UploadConstant.java
@@ -0,0 +1,21 @@
+package io.geekidea.boot.common.constant;
+
+/**
+ * 文件上传常量
+ *
+ * @author geekidea
+ * @date 2023/11/26
+ **/
+public interface UploadConstant {
+
+    String IMAGE_TYPE = "image";
+    String AUDIO_TYPE = "audio";
+    String VIDEO_TYPE = "video";
+    String XLS_TYPE = "application/vnd.ms-excel";
+    String XLSX_TYPE = "application/vnd.openxml";
+    String DOC_TYPE = "application/msword";
+    String PPT_TYPE = "application/vnd.ms-powerpoint";
+    String PDF_TYPE = "application/pdf";
+    String OFFICE_TYPE = "application/vnd.openxml";
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/controller/UploadController.java b/src/main/java/io/geekidea/boot/common/controller/UploadController.java
new file mode 100644
index 00000000..3fffb45b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/controller/UploadController.java
@@ -0,0 +1,66 @@
+package io.geekidea.boot.common.controller;
+
+import io.geekidea.boot.auth.annotation.Login;
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.common.service.UploadService;
+import io.geekidea.boot.common.vo.UploadVo;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2023/6/18
+ **/
+@Slf4j
+@Login
+@RestController
+@RequestMapping("/common/upload")
+@Tag(name = "文件上传")
+public class UploadController {
+
+    @Autowired
+    private UploadService uploadService;
+
+    /**
+     * 单个文件上传
+     *
+     * @param type
+     * @param multipartFile
+     * @return
+     * @throws Exception
+     */
+    @PostMapping(consumes = "multipart/form-data")
+    @Operation(summary = "单个文件上传")
+    @Permission("sys:file:upload")
+    public ApiResult<UploadVo> upload(@RequestParam(required = false) String type,
+                                      @RequestPart("file") MultipartFile multipartFile) throws Exception {
+        UploadVo uploadVo = uploadService.upload(type, multipartFile);
+        return ApiResult.success(uploadVo);
+    }
+
+    /**
+     * 多个文件上传
+     * 请使用swaggerUI测试多文件上传
+     *
+     * @param type
+     * @param multipartFiles
+     * @return
+     * @throws Exception
+     */
+    @PostMapping(value = "/batch", consumes = "multipart/form-data")
+    @Operation(summary = "多个文件上传")
+    @Permission("sys:file:upload-batch")
+    public ApiResult<List<UploadVo>> uploadBatch(@RequestParam(required = false) String type,
+                                                 @RequestPart("files") List<MultipartFile> multipartFiles) throws Exception {
+        List<UploadVo> uploadVos = uploadService.uploadBatch(type, multipartFiles);
+        return ApiResult.success(uploadVos);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/enums/FileServerType.java b/src/main/java/io/geekidea/boot/common/enums/FileServerType.java
new file mode 100644
index 00000000..2feb7030
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/enums/FileServerType.java
@@ -0,0 +1,64 @@
+package io.geekidea.boot.common.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 系统类型
+ *
+ * @author geekidea
+ * @date 2023/11/15
+ **/
+public enum FileServerType {
+
+    LOCAL(1, "本地文件服务"),
+    OSS(2, "OSS文件服务");
+
+    private Integer code;
+    private String desc;
+
+    FileServerType(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, FileServerType> map = new HashMap<>();
+
+    static {
+        for (FileServerType type : values()) {
+            map.put(type.code, type);
+        }
+    }
+
+    public static Integer getCode(FileServerType type) {
+        if (type == null) {
+            return LOCAL.code;
+        }
+        return type.code;
+    }
+
+    public static FileServerType get(Integer code) {
+        FileServerType type = map.get(code);
+        if (type == null) {
+            return LOCAL;
+        }
+        return type;
+    }
+
+    public static String getDesc(Integer code) {
+        FileServerType fileServerType = get(code);
+        if (fileServerType == null) {
+            return null;
+        }
+        return fileServerType.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/enums/SysFileType.java b/src/main/java/io/geekidea/boot/common/enums/SysFileType.java
new file mode 100644
index 00000000..7d4d7b33
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/enums/SysFileType.java
@@ -0,0 +1,66 @@
+package io.geekidea.boot.common.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 文件类型 1：图片，2：音视频，3：文档，4：文件
+ *
+ * @author geekidea
+ * @date 2023/11/27
+ **/
+public enum SysFileType {
+
+    IMAGE(1, "图片"),
+    VIDEO(2, "音视频"),
+    OFFICE(3, "文档"),
+    FILE(4, "文件");
+
+    private Integer code;
+    private String desc;
+
+    SysFileType(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, SysFileType> map = new HashMap<>();
+
+    static {
+        for (SysFileType type : values()) {
+            map.put(type.code, type);
+        }
+    }
+
+    public static Integer getCode(SysFileType type) {
+        if (type == null) {
+            return null;
+        }
+        return type.code;
+    }
+
+    public static SysFileType get(Integer code) {
+        SysFileType type = map.get(code);
+        if (type == null) {
+            return null;
+        }
+        return type;
+    }
+
+    public static String getDesc(Integer code) {
+        SysFileType type = get(code);
+        if (type == null) {
+            return null;
+        }
+        return type.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/enums/SysLogType.java b/src/main/java/io/geekidea/boot/common/enums/SysLogType.java
new file mode 100644
index 00000000..92ca2e43
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/enums/SysLogType.java
@@ -0,0 +1,74 @@
+package io.geekidea.boot.common.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 系统日志类型
+ * 0:访问日志,1:新增,2:修改,3:删除,4:详情,5:所有列表,6:分页列表,7:其它查询,8:上传文件,9:登录,10:退出
+ *
+ * @author geekidea
+ * @date 2023/2/15
+ **/
+public enum SysLogType {
+
+    OTHER(0, "访问日志"),
+    ADD(1, "新增"),
+    UPDATE(2, "修改"),
+    DELETE(3, "删除"),
+    INFO(4, "详情查询"),
+    ALL_LIST(5, "所有列表"),
+    LIST(6, "分页列表"),
+    OTHER_QUERY(7, "其它查询"),
+    UPLOAD(8, "上传文件"),
+    Login(9, "登录"),
+    LOGOUT(10, "退出");
+
+    private Integer code;
+    private String desc;
+
+    SysLogType(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, SysLogType> map = new HashMap<>();
+
+    static {
+        for (SysLogType type : values()) {
+            map.put(type.code, type);
+        }
+    }
+
+    public static Integer getCode(SysLogType type) {
+        if (type == null) {
+            return null;
+        }
+        return type.code;
+    }
+
+    public static SysLogType get(Integer code) {
+        SysLogType type = map.get(code);
+        if (type == null) {
+            return null;
+        }
+        return type;
+    }
+
+    public static String getDesc(Integer code) {
+        SysLogType type = get(code);
+        if (type == null) {
+            return null;
+        }
+        return type.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/enums/SystemType.java b/src/main/java/io/geekidea/boot/common/enums/SystemType.java
new file mode 100644
index 00000000..2ec27389
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/enums/SystemType.java
@@ -0,0 +1,64 @@
+package io.geekidea.boot.common.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 系统类型
+ *
+ * @author geekidea
+ * @date 2023/11/15
+ **/
+public enum SystemType {
+
+    ADMIN(1, "Admin管理后台"),
+    APP(2, "APP移动端端");
+
+    private Integer code;
+    private String desc;
+
+    SystemType(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, SystemType> map = new HashMap<>();
+
+    static {
+        for (SystemType type : values()) {
+            map.put(type.code, type);
+        }
+    }
+
+    public static Integer getCode(SystemType type) {
+        if (type == null) {
+            return null;
+        }
+        return type.code;
+    }
+
+    public static SystemType get(Integer code) {
+        SystemType type = map.get(code);
+        if (type == null) {
+            return null;
+        }
+        return type;
+    }
+
+    public static String getDesc(Integer code) {
+        SystemType type = get(code);
+        if (type == null) {
+            return null;
+        }
+        return type.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/enums/UploadType.java b/src/main/java/io/geekidea/boot/common/enums/UploadType.java
new file mode 100644
index 00000000..c2c4bda4
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/enums/UploadType.java
@@ -0,0 +1,86 @@
+package io.geekidea.boot.common.enums;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 文件上传业务类型
+ *
+ * @author geekidea
+ * @date 2023/11/26
+ **/
+public enum UploadType {
+
+    ANY("any", 100, null, "任意文件不限制上传"),
+    IMAGE("image", 20, Arrays.asList("jpg", "jpeg", "png"), "仅图片格式上传"),
+    EXCEL("excel", 100, Arrays.asList("xls", "xlxs"), "仅Excel表格上传"),
+    WORD("word", 100, Arrays.asList("doc", "docx"), "仅Word文档上传"),
+    PDF("pdf", 100, Arrays.asList("pdf"), "仅PDF上传"),
+    HEAD("head", 10, Arrays.asList("jpg", "jpeg", "png"), "用户头像上传");
+    // 更多自定义即可
+
+    private String type;
+    private Integer maxSizeMb;
+    /**
+     * 文件后缀都使用小写
+     */
+    private List<String> extensions;
+    private String desc;
+
+    UploadType(String type, Integer maxSizeMb, List<String> extensions, String desc) {
+        this.type = type;
+        this.maxSizeMb = maxSizeMb;
+        this.extensions = extensions;
+        this.desc = desc;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public Integer getMaxSizeMb() {
+        return maxSizeMb;
+    }
+
+    public List<String> getExtensions() {
+        return extensions;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<String, UploadType> map = new HashMap<>();
+
+    static {
+        for (UploadType uploadType : values()) {
+            map.put(uploadType.type, uploadType);
+        }
+    }
+
+    public static String getCode(UploadType type) {
+        if (type == null) {
+            return null;
+        }
+        return type.type;
+    }
+
+    public static UploadType get(String code) {
+        UploadType uploadType = map.get(code);
+        if (uploadType == null) {
+            return null;
+        }
+        return uploadType;
+    }
+
+    public static String getDesc(String code) {
+        UploadType uploadType = get(code);
+        if (uploadType == null) {
+            return null;
+        }
+        return uploadType.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/service/UploadService.java b/src/main/java/io/geekidea/boot/common/service/UploadService.java
new file mode 100644
index 00000000..3d532db1
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/service/UploadService.java
@@ -0,0 +1,34 @@
+package io.geekidea.boot.common.service;
+
+import io.geekidea.boot.common.vo.UploadVo;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2023/11/26
+ **/
+public interface UploadService {
+
+    /**
+     * 上传单个文件
+     *
+     * @param type
+     * @param multipartFile
+     * @return
+     * @throws Exception
+     */
+    UploadVo upload(String type, MultipartFile multipartFile) throws Exception;
+
+    /**
+     * 批量上传文件
+     *
+     * @param type
+     * @param multipartFiles
+     * @return
+     * @throws Exception
+     */
+    List<UploadVo> uploadBatch(String type, List<MultipartFile> multipartFiles) throws Exception;
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/service/impl/UploadServiceImpl.java b/src/main/java/io/geekidea/boot/common/service/impl/UploadServiceImpl.java
new file mode 100644
index 00000000..f94d1514
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/service/impl/UploadServiceImpl.java
@@ -0,0 +1,202 @@
+package io.geekidea.boot.common.service.impl;
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.NumberUtil;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import io.geekidea.boot.auth.util.CommonLoginUtil;
+import io.geekidea.boot.common.constant.UploadConstant;
+import io.geekidea.boot.common.enums.FileServerType;
+import io.geekidea.boot.common.enums.SysFileType;
+import io.geekidea.boot.common.enums.SystemType;
+import io.geekidea.boot.common.enums.UploadType;
+import io.geekidea.boot.common.service.UploadService;
+import io.geekidea.boot.common.vo.UploadVo;
+import io.geekidea.boot.config.properties.FileProperties;
+import io.geekidea.boot.config.properties.LocalFileProperties;
+import io.geekidea.boot.framework.exception.UploadException;
+import io.geekidea.boot.system.entity.SysFile;
+import io.geekidea.boot.system.service.SysFileService;
+import io.geekidea.boot.util.IpRegionUtil;
+import io.geekidea.boot.util.IpUtil;
+import io.geekidea.boot.util.SystemTypeUtil;
+import io.geekidea.boot.util.TraceIdUtil;
+import io.geekidea.boot.util.api.OssApi;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2023/11/26
+ **/
+@Slf4j
+@Service
+public class UploadServiceImpl implements UploadService {
+
+    @Autowired
+    private FileProperties fileProperties;
+
+    @Autowired
+    private LocalFileProperties localFileProperties;
+
+    @Autowired
+    private SysFileService sysFileService;
+
+    @Override
+    public UploadVo upload(String type, MultipartFile multipartFile) throws Exception {
+        log.info("文件上传开始");
+        if (StringUtils.isBlank(type)) {
+            type = UploadType.ANY.getType();
+        }
+        String originalFilename = multipartFile.getOriginalFilename();
+        log.info("originalFilename:" + originalFilename);
+        String contentType = multipartFile.getContentType();
+        log.info("contentType:" + contentType);
+        long size = multipartFile.getSize();
+        log.info("size:" + size);
+        double mb = size * 1.00 / 1024 / 1024;
+        BigDecimal sizeMb = NumberUtil.round(mb, 2);
+        log.info("sizeMb:" + sizeMb);
+        // 获取文件后缀
+        String extension = FilenameUtils.getExtension(originalFilename);
+        // 校验业务类型
+        checkUploadType(type, sizeMb, extension);
+        // 上传目录名称：年月
+        String uploadDirName = DateUtil.format(new Date(), DatePattern.SIMPLE_MONTH_PATTERN);
+        // 文件名称：年月日时分秒+15位唯一ID
+        String formatDate = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
+        String newFileName = formatDate + IdWorker.getId() + "." + extension;
+        log.info("newFileName:" + newFileName);
+        String url;
+        File uploadFile = null;
+        FileServerType fileServerType = fileProperties.getFileServerType();
+        if (FileServerType.OSS == fileServerType) {
+            // 阿里云OSS文件上传
+            InputStream inputStream = multipartFile.getInputStream();
+            url = OssApi.upload(inputStream, uploadDirName, newFileName);
+        } else {
+            // 本地文件上传路径
+            String uploadPath = localFileProperties.getUploadPath();
+            File uploadRootDir = new File(uploadPath);
+            // 上传目录不存在，则直接创建
+            if (!uploadRootDir.exists()) {
+                uploadRootDir.mkdirs();
+            }
+            File uploadDir = new File(uploadRootDir, uploadDirName);
+            if (!uploadDir.exists()) {
+                uploadDir.mkdirs();
+            }
+            // 上传文件到本地目录
+            uploadFile = new File(uploadDir, newFileName);
+            log.info("uploadFile:" + uploadFile);
+            multipartFile.transferTo(uploadFile);
+            // 返回本地文件访问路径
+            url = localFileProperties.getAccessUrl() + "/" + uploadDirName + "/" + newFileName;
+            log.info("url:" + url);
+        }
+        // 保存文件记录
+        SysFile sysFile = new SysFile();
+        sysFile.setTraceId(TraceIdUtil.getTraceId());
+        sysFile.setServerType(fileServerType.getCode());
+        sysFile.setUploadType(type);
+        sysFile.setDirName(uploadDirName);
+        sysFile.setOriginalFileName(originalFilename);
+        sysFile.setFileName(newFileName);
+        sysFile.setContentType(contentType);
+        sysFile.setExtension(extension);
+        sysFile.setSize(size);
+        sysFile.setSizeMb(sizeMb);
+        sysFile.setUrl(url);
+        // 系统类型
+        SystemType systemType = SystemTypeUtil.getSystemTypeByToken();
+        if (systemType != null) {
+            sysFile.setSystemType(systemType.getCode());
+            sysFile.setUserId(CommonLoginUtil.getUserId(systemType));
+        }
+        // 文件物理路径，本地文件服务时才有
+        if (uploadFile != null) {
+            sysFile.setFilePath(uploadFile.getPath());
+        }
+        // 文件类型
+        Integer fileType = getFileTypeByContentType(contentType);
+        sysFile.setFileType(fileType);
+        // 请求IP和区域信息
+        String requestIp = IpUtil.getRequestIp();
+        String ipAreaDesc = IpRegionUtil.getIpAreaDesc(requestIp);
+        sysFile.setIp(requestIp);
+        sysFile.setIpArea(ipAreaDesc);
+        // 保存文件记录
+        sysFileService.save(sysFile);
+        UploadVo uploadVo = new UploadVo();
+        uploadVo.setId(sysFile.getId());
+        uploadVo.setOriginalFilename(originalFilename);
+        uploadVo.setFilename(newFileName);
+        uploadVo.setUrl(url);
+        uploadVo.setSize(size);
+        uploadVo.setSizeMb(sizeMb);
+        log.info("文件上传结束");
+        return uploadVo;
+    }
+
+    private Integer getFileTypeByContentType(String contentType) {
+        if (StringUtils.isBlank(contentType)) {
+            return SysFileType.FILE.getCode();
+        }
+        if (contentType.startsWith(UploadConstant.IMAGE_TYPE)) {
+            return SysFileType.IMAGE.getCode();
+        } else if (contentType.startsWith(UploadConstant.AUDIO_TYPE) || contentType.startsWith(UploadConstant.VIDEO_TYPE)) {
+            return SysFileType.VIDEO.getCode();
+        } else if (contentType.startsWith(UploadConstant.XLS_TYPE) ||
+                contentType.startsWith(UploadConstant.XLSX_TYPE) ||
+                contentType.startsWith(UploadConstant.DOC_TYPE) ||
+                contentType.startsWith(UploadConstant.PPT_TYPE) ||
+                contentType.startsWith(UploadConstant.PDF_TYPE) ||
+                contentType.startsWith(UploadConstant.OFFICE_TYPE)
+        ) {
+            return SysFileType.OFFICE.getCode();
+        } else {
+            return SysFileType.FILE.getCode();
+        }
+    }
+
+    @Override
+    public List<UploadVo> uploadBatch(String type, List<MultipartFile> multipartFiles) throws Exception {
+        log.info("批量文件上传结束");
+        List<UploadVo> uploadVos = new ArrayList<>();
+        for (MultipartFile multipartFile : multipartFiles) {
+            UploadVo upload = upload(type, multipartFile);
+            uploadVos.add(upload);
+        }
+        log.info("批量文件上传结束");
+        return uploadVos;
+    }
+
+    private void checkUploadType(String type, BigDecimal sizeMb, String extension) throws Exception {
+        UploadType uploadType = UploadType.get(type);
+        System.out.println("uploadType = " + uploadType);
+        if (UploadType.ANY == uploadType) {
+            return;
+        }
+        Integer maxSizeMb = uploadType.getMaxSizeMb();
+        List<String> extensions = uploadType.getExtensions();
+        if (sizeMb.doubleValue() > maxSizeMb) {
+            throw new UploadException("超过文件大小限制，请上传" + maxSizeMb + "MB以内的文件");
+        }
+        extension = extension.toLowerCase();
+        if (!extensions.contains(extension)) {
+            throw new UploadException("文件格式错误，请上传" + extensions.toString() + "格式的文件");
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/common/vo/UploadVo.java b/src/main/java/io/geekidea/boot/common/vo/UploadVo.java
new file mode 100644
index 00000000..e2056007
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/common/vo/UploadVo.java
@@ -0,0 +1,34 @@
+package io.geekidea.boot.common.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * @author geekidea
+ * @date 2023/11/26
+ **/
+@Data
+@Schema(description = "文件上传VO")
+public class UploadVo {
+
+    @Schema(description = "文件ID")
+    private Long id;
+
+    @Schema(description = "源文件名称")
+    private String originalFilename;
+
+    @Schema(description = "文件名称")
+    private String filename;
+
+    @Schema(description = "文件路径")
+    private String url;
+
+    @Schema(description = "文件大小")
+    private Long size;
+
+    @Schema(description = "文件大小MB")
+    private BigDecimal sizeMb;
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/CorsConfig.java b/src/main/java/io/geekidea/boot/config/CorsConfig.java
new file mode 100644
index 00000000..05214f6e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/CorsConfig.java
@@ -0,0 +1,43 @@
+package io.geekidea.boot.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import java.util.Arrays;
+
+/**
+ * @author geekidea
+ * @date 2023/2/16
+ **/
+@Slf4j
+@Configuration
+public class CorsConfig {
+
+    /**
+     * CORS跨域设置
+     *
+     * @return
+     */
+    @Bean
+    public CorsFilter corsFilter() {
+        CorsConfiguration corsConfiguration = new CorsConfiguration();
+        // 允许访问的源
+        corsConfiguration.addAllowedOriginPattern("*");
+        // 允许访问的请求头
+        corsConfiguration.addAllowedHeader("*");
+        // 允许访问的请求方式
+        corsConfiguration.setAllowedMethods(Arrays.asList("OPTION", "GET", "POST"));
+        // 是否允许发送cookie
+        corsConfiguration.setAllowCredentials(true);
+        // 允许响应的头
+        corsConfiguration.addExposedHeader("*");
+        UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();
+        configurationSource.registerCorsConfiguration("/**", corsConfiguration);
+        return new CorsFilter(configurationSource);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/JacksonConfig.java b/src/main/java/io/geekidea/boot/config/JacksonConfig.java
new file mode 100644
index 00000000..2ba45b6c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/JacksonConfig.java
@@ -0,0 +1,55 @@
+package io.geekidea.boot.config;
+
+import cn.hutool.core.date.DatePattern;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import io.geekidea.boot.framework.jackson.deserializer.JacksonDateDeserializer;
+import io.geekidea.boot.framework.jackson.deserializer.JacksonStringDeserializer;
+import io.geekidea.boot.framework.jackson.serializer.JacksonStringSerializer;
+import io.geekidea.boot.framework.xss.XssJacksonDeserializer;
+import io.geekidea.boot.framework.xss.XssJacksonSerializer;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.time.ZoneId;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * @author geekidea
+ * @date 2022/4/13
+ **/
+@Configuration
+public class JacksonConfig {
+
+    @Value("${xss.enable}")
+    private boolean enableXss;
+
+    @Bean
+    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
+        return builder -> {
+            builder.locale(Locale.CHINA);
+            builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
+            builder.simpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);
+            // 反序列化(处理请求参数)
+            // 去掉请求参数中字符串左右两边的空格
+            builder.deserializerByType(String.class, JacksonStringDeserializer.INSTANCE);
+            builder.deserializerByType(Date.class, JacksonDateDeserializer.INSTANCE);
+            // 序列化(处理响应结果)
+            // 避免long类型精度丢失，将long类型序列化成字符串
+            builder.serializerByType(Long.class, ToStringSerializer.instance);
+            // 去掉响应结果中字符串左右两边的空格
+            builder.serializerByType(String.class, JacksonStringSerializer.INSTANCE);
+
+            // XSS序列化
+            if (enableXss) {
+                builder.serializerByType(String.class, new XssJacksonSerializer());
+                builder.deserializerByType(String.class, new XssJacksonDeserializer());
+            }
+
+        };
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/MybatisPlusConfig.java b/src/main/java/io/geekidea/boot/config/MybatisPlusConfig.java
new file mode 100644
index 00000000..d59aa847
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/MybatisPlusConfig.java
@@ -0,0 +1,104 @@
+package io.geekidea.boot.config;
+
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
+import io.geekidea.boot.auth.util.LoginUtil;
+import io.geekidea.boot.config.properties.MerchantLineProperties;
+import io.geekidea.boot.framework.mybatis.plugins.MerchantLineInnerInterceptor;
+import io.geekidea.boot.framework.mybatis.plugins.handler.DataScopeHandler;
+import io.geekidea.boot.framework.mybatis.plugins.handler.MerchantLineHandler;
+import io.geekidea.boot.util.SystemTypeUtil;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.LongValue;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+/**
+ * MybatisPlus配置
+ *
+ * @author geekidea
+ * @date 2023/11/25
+ **/
+@Slf4j
+@Configuration
+public class MybatisPlusConfig {
+
+    @Autowired
+    private MerchantLineProperties merchantLineProperties;
+
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        // 数据范围权限
+        interceptor.addInnerInterceptor(new DataPermissionInterceptor(new DataScopeHandler()));
+        // 多商户插件，默认关闭，如有需要，放开注释即可
+        // interceptor.addInnerInterceptor(new MerchantLineInnerInterceptor(merchantLineHandler()));
+        // 攻击 SQL 阻断解析器,防止全表更新与删除
+        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
+        return interceptor;
+    }
+
+    /**
+     * 配置多商户插件
+     * 如果不需要删除即可
+     *
+     * @return
+     */
+    @Bean
+    public MerchantLineHandler merchantLineHandler() {
+        log.info("merchantLineProperties:" + merchantLineProperties);
+        return new MerchantLineHandler() {
+            @Override
+            public Expression getMerchantId() {
+                // TODO 可以在LoginUtil中添加获取商户ID的方法
+                // LoginUtil.getMerchantId();
+                return new LongValue(1);
+            }
+
+            @Override
+            public String getMerchantIdColumn() {
+                return merchantLineProperties.getMerchantIdColumn();
+            }
+
+            @Override
+            public boolean ignoreTable(String tableName) {
+                List<String> includeTables = merchantLineProperties.getIncludeTables();
+                // 判断如果包含指定的表，则不忽略，否则忽略
+                if (CollectionUtils.isNotEmpty(includeTables)) {
+                    if (includeTables.contains(tableName)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            /**
+             * 判断系统类型和系统角色，是否忽略添加商户ID
+             * 添加商户ID的情况
+             * 1：是管理后台的请求
+             * 2：非管理员
+             * 除此之外，都忽略
+             *
+             * @return
+             */
+            @Override
+            public boolean ignoreMerchantId() {
+                try {
+                    if (SystemTypeUtil.isAdminSystem() && LoginUtil.isNotAdmin()) {
+                        return false;
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                return true;
+            }
+        };
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/OpenApiConfig.java b/src/main/java/io/geekidea/boot/config/OpenApiConfig.java
new file mode 100644
index 00000000..4d11b6b3
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/OpenApiConfig.java
@@ -0,0 +1,128 @@
+package io.geekidea.boot.config;
+
+import io.geekidea.boot.common.constant.LoginConstant;
+import io.geekidea.boot.config.properties.OpenApiProperties;
+import io.swagger.v3.oas.models.ExternalDocumentation;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Contact;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.media.StringSchema;
+import io.swagger.v3.oas.models.parameters.HeaderParameter;
+import io.swagger.v3.oas.models.parameters.Parameter;
+import org.springdoc.core.GroupedOpenApi;
+import org.springdoc.core.customizers.OperationCustomizer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author geekidea
+ * @date 2022/10/27
+ **/
+@Configuration
+public class OpenApiConfig {
+
+    private static final String TOKEN_NAME = LoginConstant.TOKEN_NAME;
+
+    private static String AUTH_PACKAGE = "io.geekidea.boot.auth";
+    private static String SYSTEM_PACKAGE = "io.geekidea.boot.system";
+    private static String USER_PACKAGE = "io.geekidea.boot.user";
+    private static String COMMON_PACKAGE = "io.geekidea.boot.common";
+    private static String GENERATOR_PACKAGE = "io.geekidea.boot.generator";
+
+    @Autowired
+    private OpenApiProperties openApiProperties;
+
+    /**
+     * token请求头参数
+     */
+    private Parameter tokenParameter = new HeaderParameter().name(TOKEN_NAME).required(false).schema(new StringSchema()._default("").name(TOKEN_NAME));
+
+
+    @Bean
+    public OpenAPI openApi() {
+        return new OpenAPI()
+                .info(new Info()
+                        .title(openApiProperties.getTitle())
+                        .description(openApiProperties.getDescription())
+                        .termsOfService(openApiProperties.getTermsOfService())
+                        .contact(new Contact().name(openApiProperties.getContactName()).url(openApiProperties.getContactUrl()).email(openApiProperties.getContactEmail()))
+                        .version(openApiProperties.getVersion()))
+                .externalDocs(new ExternalDocumentation().description(openApiProperties.getExternalDescription()).url(openApiProperties.getExternalUrl()));
+    }
+
+
+    @Bean
+    public GroupedOpenApi authApi() {
+        String[] packagedToMatch = {AUTH_PACKAGE};
+        return api("2.登录授权接口文档", packagedToMatch);
+    }
+
+    @Bean
+    public GroupedOpenApi adminApi() {
+        String[] packagedToMatch = {SYSTEM_PACKAGE};
+        return api("3.系统管理接口文档", packagedToMatch);
+    }
+
+    @Bean
+    public GroupedOpenApi userApi() {
+        String[] packagedToMatch = {USER_PACKAGE};
+        return api("4.App用户模块接口文档", packagedToMatch);
+    }
+
+    @Bean
+    public GroupedOpenApi commonApi() {
+        String[] packagedToMatch = {COMMON_PACKAGE};
+        return api("5.公共服务接口文档", packagedToMatch);
+    }
+
+    @Bean
+    public GroupedOpenApi generatorApi() {
+        String[] packagedToMatch = {GENERATOR_PACKAGE};
+        return api("6.生成代码接口文档", packagedToMatch);
+    }
+
+    /**
+     * 除了上面的接口之外，其它的接口都在项目接口文档中
+     * 请根据实际情况进行自定义
+     *
+     * @return
+     */
+    @Bean
+    public GroupedOpenApi projectApi() {
+        return GroupedOpenApi.builder()
+                .group("1.项目接口文档")
+                .addOperationCustomizer(getOperationCustomizer())
+                .pathsToMatch("/**")
+                .packagesToExclude(AUTH_PACKAGE, SYSTEM_PACKAGE, USER_PACKAGE, COMMON_PACKAGE, GENERATOR_PACKAGE)
+                .build();
+    }
+
+    /**
+     * 配置接口
+     *
+     * @param group
+     * @param packagedToMatch
+     * @return
+     */
+    private GroupedOpenApi api(String group, String[] packagedToMatch) {
+        return GroupedOpenApi.builder()
+                .group(group)
+                .addOperationCustomizer(getOperationCustomizer())
+                .pathsToMatch("/**")
+                .packagesToScan(packagedToMatch).build();
+    }
+
+    /**
+     * 配置自定义请求头
+     *
+     * @return
+     */
+    public OperationCustomizer getOperationCustomizer() {
+        return (operation, handlerMethod) -> {
+            operation.addParametersItem(tokenParameter);
+            return operation;
+        };
+    }
+
+}
diff --git a/bootstrap/src/main/java/io/geekidea/springbootplus/config/RedisTemplateConfig.java b/src/main/java/io/geekidea/boot/config/RedisConfig.java
similarity index 55%
rename from bootstrap/src/main/java/io/geekidea/springbootplus/config/RedisTemplateConfig.java
rename to src/main/java/io/geekidea/boot/config/RedisConfig.java
index 7e66baba..b030f166 100644
--- a/bootstrap/src/main/java/io/geekidea/springbootplus/config/RedisTemplateConfig.java
+++ b/src/main/java/io/geekidea/boot/config/RedisConfig.java
@@ -1,21 +1,7 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.config;
+package io.geekidea.boot.config;
 
+import io.geekidea.boot.framework.redis.CustomStringRedisSerializer;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
@@ -24,37 +10,33 @@
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
 /**
- * <p>
  * Redis Template 配置
- * </p>
  *
  * @author geekidea
- * @date 2018-11-08
+ * @date 2022-1-7
  */
 @Configuration
-public class RedisTemplateConfig {
+public class RedisConfig {
+
+    @Value("${redis.projectPrefix}")
+    private String projectPrefix;
 
     @Bean
     public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
         RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
         redisTemplate.setConnectionFactory(redisConnectionFactory);
-
         // 自定义的string序列化器和fastjson序列化器
-        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
-
+        CustomStringRedisSerializer customStringRedisSerializer = new CustomStringRedisSerializer(projectPrefix);
         // jackson 序列化器
         GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
-
         // kv 序列化
-        redisTemplate.setKeySerializer(stringRedisSerializer);
+        redisTemplate.setKeySerializer(customStringRedisSerializer);
         redisTemplate.setValueSerializer(jsonRedisSerializer);
-
         // hash 序列化
-        redisTemplate.setHashKeySerializer(stringRedisSerializer);
+        redisTemplate.setHashKeySerializer(customStringRedisSerializer);
         redisTemplate.setHashValueSerializer(jsonRedisSerializer);
-
         redisTemplate.afterPropertiesSet();
-
         return redisTemplate;
     }
+
 }
diff --git a/src/main/java/io/geekidea/boot/config/WebMvcConfig.java b/src/main/java/io/geekidea/boot/config/WebMvcConfig.java
new file mode 100644
index 00000000..8255756c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/WebMvcConfig.java
@@ -0,0 +1,185 @@
+package io.geekidea.boot.config;
+
+import io.geekidea.boot.auth.interceptor.*;
+import io.geekidea.boot.config.properties.*;
+import io.geekidea.boot.framework.filter.JsonRequestBodyFilter;
+import io.geekidea.boot.framework.filter.TraceIdLogFilter;
+import io.geekidea.boot.framework.interceptor.PageHelperClearInterceptor;
+import io.geekidea.boot.framework.xss.XssFilter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2022/3/15
+ **/
+@Slf4j
+@Configuration
+public class WebMvcConfig implements WebMvcConfigurer {
+
+    @Autowired
+    private LoginProperties loginProperties;
+
+    @Autowired
+    private LoginAdminProperties loginAdminProperties;
+
+    @Autowired
+    private LoginAppProperties loginAppProperties;
+
+    @Autowired
+    private LoginCommonProperties loginCommonProperties;
+
+    @Autowired
+    private LocalFileProperties localFileProperties;
+
+    @Autowired
+    private XssProperties xssProperties;
+
+    @Autowired
+    private NotAuthProperties notAuthProperties;
+
+    @Bean
+    public ExcludePathInterceptor excludePathInterceptor() {
+        return new ExcludePathInterceptor();
+    }
+
+    @Bean
+    public NotAuthInterceptor notAuthInterceptor() {
+        return new NotAuthInterceptor();
+    }
+
+    @Bean
+    public TokenInterceptor tokenInterceptor() {
+        return new TokenInterceptor();
+    }
+
+    @Bean
+    public LoginInterceptor loginInterceptor() {
+        return new LoginInterceptor();
+    }
+
+    @Bean
+    public AppLoginInterceptor loginAppInterceptor() {
+        return new AppLoginInterceptor();
+    }
+
+    @Bean
+    public CommonLoginInterceptor loginCommonInterceptor() {
+        return new CommonLoginInterceptor();
+    }
+
+    @Bean
+    public RefreshTokenInterceptor refreshTokenInterceptor() {
+        return new RefreshTokenInterceptor();
+    }
+
+    @Bean
+    public PageHelperClearInterceptor pageHelperClearInterceptor() {
+        return new PageHelperClearInterceptor();
+    }
+
+
+    @Bean
+    public FilterRegistrationBean traceIdLogFilter() {
+        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
+        TraceIdLogFilter traceIdLogFilter = new TraceIdLogFilter();
+        filterRegistrationBean.setFilter(traceIdLogFilter);
+        filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
+        return filterRegistrationBean;
+    }
+
+    @Bean
+    public FilterRegistrationBean jsonRequestBodyFilter() {
+        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
+        JsonRequestBodyFilter jsonRequestBodyFilter = new JsonRequestBodyFilter();
+        filterRegistrationBean.setFilter(jsonRequestBodyFilter);
+        List<String> urls = new ArrayList<>();
+        urls.add("/*");
+        filterRegistrationBean.setUrlPatterns(urls);
+        return filterRegistrationBean;
+    }
+
+    /**
+     * XssFilter配置
+     *
+     * @return
+     */
+    @Bean
+    public FilterRegistrationBean xssFilter() {
+        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
+        filterRegistrationBean.setFilter(new XssFilter());
+        filterRegistrationBean.setEnabled(xssProperties.isEnable());
+        filterRegistrationBean.addUrlPatterns(xssProperties.getUrlPatterns());
+        filterRegistrationBean.setOrder(xssProperties.getOrder());
+        filterRegistrationBean.setAsyncSupported(xssProperties.isAsync());
+        return filterRegistrationBean;
+    }
+
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        // 上传文件访问路径
+        String accessPath = localFileProperties.getAccessPath();
+        // 上传文件保存路径
+        String uploadPath = localFileProperties.getUploadPath();
+        if (StringUtils.isNotBlank(accessPath) && StringUtils.isNotBlank(uploadPath)) {
+            // 虚拟目录文件映射
+            registry.addResourceHandler(accessPath)
+                    .addResourceLocations("file:" + uploadPath);
+        }
+    }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        // 加入的顺序就是拦截器执行的顺序
+        registry.addInterceptor(excludePathInterceptor());
+        // 没有权限的拦截器，直接返回无权限
+        boolean enableNotAuth = notAuthProperties.isEnable();
+        if (enableNotAuth) {
+            List<String> includePaths = notAuthProperties.getIncludePaths();
+            registry.addInterceptor(notAuthInterceptor()).addPathPatterns(includePaths);
+        }
+
+        // token拦截器
+        registry.addInterceptor(tokenInterceptor()).excludePathPatterns("/admin/login", "/app/login");
+        // 管理后台登录拦截器配置
+        boolean enableAdminInterceptor = loginAdminProperties.isEnable();
+        if (enableAdminInterceptor) {
+            List<String> excludePaths = loginProperties.getExcludePaths();
+            List<String> adminExcludePaths = loginAdminProperties.getExcludePaths();
+            adminExcludePaths.addAll(excludePaths);
+            registry.addInterceptor(loginInterceptor())
+                    .addPathPatterns(loginAdminProperties.getIncludePaths())
+                    .excludePathPatterns(adminExcludePaths);
+        }
+        // 移动端端登录拦截器配置
+        boolean enableAppInterceptor = loginAppProperties.isEnable();
+        if (enableAppInterceptor) {
+            List<String> appIncludePaths = loginAppProperties.getIncludePaths();
+            List<String> excludePaths = loginAppProperties.getExcludePaths();
+            registry.addInterceptor(loginAppInterceptor()).addPathPatterns(appIncludePaths).excludePathPatterns(excludePaths);
+        }
+        // 刷新token拦截器
+        registry.addInterceptor(refreshTokenInterceptor());
+
+        // 系统公共请求拦截器，子拦截/common/开头的请求
+        boolean enableCommonInterceptor = loginCommonProperties.isEnable();
+        if (enableCommonInterceptor) {
+            registry.addInterceptor(loginCommonInterceptor()).addPathPatterns(loginCommonProperties.getIncludePaths());
+        }
+
+        // 分页缓存清除拦截器
+        registry.addInterceptor(pageHelperClearInterceptor());
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/BootProperties.java b/src/main/java/io/geekidea/boot/config/properties/BootProperties.java
new file mode 100644
index 00000000..33e3adb7
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/BootProperties.java
@@ -0,0 +1,31 @@
+package io.geekidea.boot.config.properties;
+
+import io.geekidea.boot.util.YamlUtil;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 框架公共配置
+ *
+ * @author geekidea
+ * @date 2023/12/03
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "boot")
+public class BootProperties {
+
+    /**
+     * 排除的路径
+     */
+    private List<String> excludePaths;
+
+    public void setExcludePaths(List<String> excludePaths) {
+        this.excludePaths = YamlUtil.parseListArray(excludePaths);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/FileProperties.java b/src/main/java/io/geekidea/boot/config/properties/FileProperties.java
new file mode 100644
index 00000000..b770e98d
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/FileProperties.java
@@ -0,0 +1,24 @@
+package io.geekidea.boot.config.properties;
+
+import io.geekidea.boot.common.enums.FileServerType;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 上传文件属性配置
+ *
+ * @author geekidea
+ * @date 2023/06/18
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "file")
+public class FileProperties {
+
+    /**
+     * 文件服务类型
+     */
+    private FileServerType fileServerType;
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/GeneratorProperties.java b/src/main/java/io/geekidea/boot/config/properties/GeneratorProperties.java
new file mode 100644
index 00000000..cb039ea3
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/GeneratorProperties.java
@@ -0,0 +1,163 @@
+package io.geekidea.boot.config.properties;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import io.geekidea.boot.generator.enums.GeneratorFormLayout;
+import io.geekidea.boot.generator.enums.GeneratorType;
+import io.geekidea.boot.generator.enums.RequestMappingStyle;
+import io.geekidea.boot.util.YamlUtil;
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 生成器默认配置
+ *
+ * @author geekidea
+ * @date 2023/12/31
+ **/
+@Data
+@Component
+@PropertySource(value = {"classpath:generator.yml"})
+@ConfigurationProperties(prefix = "generator")
+public class GeneratorProperties {
+
+    /**
+     * 包名称
+     */
+    @Value("${packageName}")
+    private String packageName;
+
+    /**
+     * 模块名称
+     */
+    @Value("${moduleName}")
+    private String moduleName;
+
+    /**
+     * 作者
+     */
+    @Value("${author}")
+    private String author;
+
+    /**
+     * 生成ID类型枚举类型
+     */
+    @Value("${idType}")
+    private IdType idType;
+
+    /**
+     * 表前缀
+     */
+    @Value("${tablePrefixes}")
+    private List<String> tablePrefixes;
+
+    /**
+     * 是否生成后台 1：是，0：否
+     */
+    @Value("${generatorBackend}")
+    private boolean generatorBackend;
+
+    /**
+     * 是否生成前端 1：是，0：否
+     */
+    @Value("${generatorFrontend}")
+    private boolean generatorFrontend;
+
+    /**
+     * 是否生成App后端代码 1：是，0：否
+     */
+    @Value("${generatorAppBackend}")
+    private boolean generatorAppBackend;
+
+    /**
+     * 是否校验字段 1：是，0：否
+     */
+    @Value("${validateField}")
+    private boolean validateField;
+
+    /**
+     * 是否启用日志注解 1：是，0：否
+     */
+    @Value("${enableLog}")
+    private boolean enableLog;
+
+    /**
+     * 是否启用权限编码 1：是，0：否
+     */
+    @Value("${enablePermission}")
+    private boolean enablePermission;
+
+    /**
+     * 请求映射风格 1：默认，2：restful，3：全部小写，4：中横线，5：下划线
+     */
+    @Value("${requestMappingStyle}")
+    private RequestMappingStyle requestMappingStyle = RequestMappingStyle.DEFAULT;
+
+
+    /**
+     * 默认降序排列名称，如id、create_time，如果为空，则不指定默认排序
+     */
+    @Value("${defaultOrderColumnName}")
+    private String defaultOrderColumnName;
+
+    /**
+     * 上级菜单ID
+     */
+    @Value("${parentMenuId}")
+    private Long parentMenuId;
+
+    /**
+     * 表单布局方式 1：一行一列，2：一行两列
+     */
+    @Value("${formLayout}")
+    private GeneratorFormLayout formLayout;
+
+    /**
+     * 生成方式 1：zip压缩包，2：自定义路径
+     */
+    @Value("${generatorType}")
+    private GeneratorType generatorType;
+
+    /**
+     * 自定义生成后端路径
+     */
+    @Value("${customBackendPath}")
+    private String customBackendPath;
+
+    /**
+     * 自定义生成前端路径
+     */
+    @Value("${customFrontendPath}")
+    private String customFrontendPath;
+
+    /**
+     * 是否显示默认查询条件
+     */
+    @Value("${showDefaultQuery}")
+    private boolean showDefaultQuery;
+
+    /**
+     * 搜索框模糊查询字段
+     */
+    @Value("${keywordFields}")
+    private List<String> keywordFields;
+
+    /**
+     * 是否只生成实体类 1：是，0：否
+     */
+    @Value("${onlyGeneratorEntity}")
+    private boolean onlyGeneratorEntity;
+
+    public void setTablePrefixes(List<String> tablePrefixes) {
+        this.tablePrefixes = YamlUtil.parseListArray(tablePrefixes);
+    }
+
+    public void setKeywordFields(List<String> keywordFields) {
+        this.keywordFields = YamlUtil.parseListArray(keywordFields);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/LocalFileProperties.java b/src/main/java/io/geekidea/boot/config/properties/LocalFileProperties.java
new file mode 100644
index 00000000..b61e5c93
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/LocalFileProperties.java
@@ -0,0 +1,51 @@
+package io.geekidea.boot.config.properties;
+
+import io.geekidea.boot.common.constant.CommonConstant;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.io.File;
+
+/**
+ * 本地文件属性配置
+ *
+ * @author geekidea
+ * @date 2023/06/18
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "file.local")
+public class LocalFileProperties {
+
+    /**
+     * 上传文件访问路径
+     */
+    private String accessPath;
+
+    /**
+     * 上传文件保存路径
+     */
+    private String uploadPath;
+
+    /**
+     * 上传文件访问URL前缀
+     */
+    private String accessUrl;
+
+    public void setUploadPath(String uploadPath) {
+        if (StringUtils.isBlank(uploadPath)) {
+            // 默认为当前项目路径下的upload目录
+            String userDir = CommonConstant.USER_DIR;
+            uploadPath = userDir + File.separator + CommonConstant.UPLOAD + File.separator;
+        } else {
+            // 如果不是以/或\\结尾，则根据系统环境加上对应的斜杠结尾
+            if (!(uploadPath.endsWith(CommonConstant.SLASH) || uploadPath.endsWith(CommonConstant.DOUBLE_BACKSLASH))) {
+                uploadPath = uploadPath + File.separator;
+            }
+        }
+        this.uploadPath = uploadPath;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/LogAopProperties.java b/src/main/java/io/geekidea/boot/config/properties/LogAopProperties.java
new file mode 100644
index 00000000..8a42a4da
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/LogAopProperties.java
@@ -0,0 +1,50 @@
+package io.geekidea.boot.config.properties;
+
+import io.geekidea.boot.framework.enums.ResponseLogType;
+import io.geekidea.boot.util.YamlUtil;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2022/6/18
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "log-aop")
+public class LogAopProperties {
+
+    /**
+     * 是否启用
+     */
+    private boolean enable = true;
+
+    /**
+     * 是否打印日志
+     */
+    private boolean printLog = true;
+
+    /**
+     * 是否打印请求头日志
+     */
+    private boolean printHeadLog = false;
+
+    /**
+     * 排除的路径
+     */
+    private List<String> excludePaths;
+
+    /**
+     * 响应日志类型：FULL 全部，PART 部分，NONE 无
+     */
+    private ResponseLogType responseLogType;
+
+
+    public void setExcludePaths(List<String> excludePaths) {
+        this.excludePaths = YamlUtil.parseListArray(excludePaths);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/LoginAdminProperties.java b/src/main/java/io/geekidea/boot/config/properties/LoginAdminProperties.java
new file mode 100644
index 00000000..d6178a7b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/LoginAdminProperties.java
@@ -0,0 +1,67 @@
+package io.geekidea.boot.config.properties;
+
+import io.geekidea.boot.common.constant.LoginConstant;
+import io.geekidea.boot.util.YamlUtil;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "login.admin")
+public class LoginAdminProperties {
+
+    /**
+     * 是否启用登录校验
+     */
+    private boolean enable;
+
+    /**
+     * 是否单次登录
+     * true: 用户最后一次登录有效，之前的登录会话下线
+     * false: 用户可多次登录，多次登录的会话都生效
+     */
+    private boolean singleLogin;
+
+    /**
+     * token过期分钟数
+     */
+    private Integer tokenExpireMinutes;
+
+    /**
+     * 方法是否鉴权
+     */
+    private boolean loginPermission;
+
+    /**
+     * 包含的路径
+     */
+    private List<String> includePaths;
+
+    /**
+     * 排除的路径
+     */
+    private List<String> excludePaths;
+
+    public void setExcludePaths(List<String> excludePaths) {
+        this.excludePaths = YamlUtil.parseListArray(excludePaths);
+    }
+
+    public void setIncludePaths(List<String> includePaths) {
+        this.includePaths = YamlUtil.parseListArray(includePaths);
+    }
+
+    public Integer getTokenExpireMinutes() {
+        if (tokenExpireMinutes == null) {
+            this.tokenExpireMinutes = LoginConstant.ADMIN_TOKEN_EXPIRE_MINUTES;
+        }
+        return tokenExpireMinutes;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/LoginAppProperties.java b/src/main/java/io/geekidea/boot/config/properties/LoginAppProperties.java
new file mode 100644
index 00000000..9aa00abb
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/LoginAppProperties.java
@@ -0,0 +1,88 @@
+package io.geekidea.boot.config.properties;
+
+import io.geekidea.boot.auth.enums.LoginInterceptStrategy;
+import io.geekidea.boot.common.constant.LoginConstant;
+import io.geekidea.boot.util.YamlUtil;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "login.app")
+public class LoginAppProperties {
+
+    /**
+     * 是否启用登录校验
+     */
+    private boolean enable;
+
+    /**
+     * 默认拦截包含路径开头的所有请求(/app/**)，在不需要验证登录的controller方法加上@IgnoreLogin，表示此方法不需要登录就能调用
+     * 默认不拦截包含路径开头的所有请求(/app/**)，在需要登录的controller方法上加上注解@Login，表示此方法必须登录才能调用
+     */
+    private LoginInterceptStrategy loginInterceptStrategy;
+
+    /**
+     * 是否单次登录
+     * true: 用户最后一次登录有效，之前的登录会话下线
+     * false: 用户可多次登录，多次登录的会话都生效
+     */
+    private boolean singleLogin;
+
+    /**
+     * token过期天数
+     */
+    private Integer tokenExpireDays;
+
+    /**
+     * 方法是否鉴权
+     */
+    private boolean loginPermission;
+
+    /**
+     * 包含的路径
+     */
+    private List<String> includePaths;
+
+    /**
+     * 排除的路径
+     */
+    private List<String> excludePaths;
+
+    /**
+     * 需要登录校验的请求路径，与@Login效果一样
+     */
+    private List<String> checkLoginPaths;
+
+    /**
+     * 忽略登录校验的请求路径，与@IgnoreLogin效果一样
+     */
+    private List<String> ignoreLoginPaths;
+
+    public void setExcludePaths(List<String> excludePaths) {
+        this.excludePaths = YamlUtil.parseListArray(excludePaths);
+    }
+
+    public Integer getTokenExpireDays() {
+        if (tokenExpireDays == null) {
+            this.tokenExpireDays = LoginConstant.APP_TOKEN_EXPIRE_DAYS;
+        }
+        return tokenExpireDays;
+    }
+
+    public void setCheckLoginPaths(List<String> checkLoginPaths) {
+        this.checkLoginPaths = YamlUtil.parseListArray(checkLoginPaths);
+    }
+
+    public void setIgnoreLoginPaths(List<String> ignoreLoginPaths) {
+        this.ignoreLoginPaths = YamlUtil.parseListArray(ignoreLoginPaths);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/LoginCommonProperties.java b/src/main/java/io/geekidea/boot/config/properties/LoginCommonProperties.java
new file mode 100644
index 00000000..0ccc1d57
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/LoginCommonProperties.java
@@ -0,0 +1,34 @@
+package io.geekidea.boot.config.properties;
+
+import io.geekidea.boot.util.YamlUtil;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "login.common")
+public class LoginCommonProperties {
+
+    /**
+     * 是否启用拦截器
+     */
+    private boolean enable;
+
+    /**
+     * 包含的路径
+     */
+    private List<String> includePaths;
+
+    public void setExcludePaths(List<String> includePaths) {
+        this.includePaths = YamlUtil.parseListArray(includePaths);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/LoginProperties.java b/src/main/java/io/geekidea/boot/config/properties/LoginProperties.java
new file mode 100644
index 00000000..8d744500
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/LoginProperties.java
@@ -0,0 +1,30 @@
+package io.geekidea.boot.config.properties;
+
+import io.geekidea.boot.util.YamlUtil;
+import lombok.Data;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "login")
+public class LoginProperties {
+
+    /**
+     * 排除的路径
+     */
+    private List<String> excludePaths;
+
+    public void setExcludePaths(List<String> excludePaths) {
+        this.excludePaths = YamlUtil.parseListArray(excludePaths);
+    }
+
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/MerchantLineProperties.java b/src/main/java/io/geekidea/boot/config/properties/MerchantLineProperties.java
new file mode 100644
index 00000000..935526d0
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/MerchantLineProperties.java
@@ -0,0 +1,35 @@
+package io.geekidea.boot.config.properties;
+
+import io.geekidea.boot.util.YamlUtil;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 多商户行级数据权限配置
+ *
+ * @author geekidea
+ * @date 2023/12/05
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "merchant-line")
+public class MerchantLineProperties {
+
+    /**
+     * 商户ID列名称
+     */
+    private String merchantIdColumn;
+
+    /**
+     * 包含的表名称
+     */
+    private List<String> includeTables;
+
+    public void setIncludeTables(List<String> includeTables) {
+        this.includeTables = YamlUtil.parseListArray(includeTables);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/NotAuthProperties.java b/src/main/java/io/geekidea/boot/config/properties/NotAuthProperties.java
new file mode 100644
index 00000000..17d942f0
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/NotAuthProperties.java
@@ -0,0 +1,35 @@
+package io.geekidea.boot.config.properties;
+
+import io.geekidea.boot.util.YamlUtil;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 没有权限属性配置
+ *
+ * @author geekidea
+ * @date 2023/06/18
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "not-auth")
+public class NotAuthProperties {
+
+    /**
+     * 是否启用登录校验
+     */
+    private boolean enable;
+
+    /**
+     * 包含的路径
+     */
+    private List<String> includePaths;
+
+    public void setExcludePaths(List<String> includePaths) {
+        this.includePaths = YamlUtil.parseListArray(includePaths);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/OpenApiProperties.java b/src/main/java/io/geekidea/boot/config/properties/OpenApiProperties.java
new file mode 100644
index 00000000..9a610ec6
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/OpenApiProperties.java
@@ -0,0 +1,63 @@
+package io.geekidea.boot.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Swagger属性配置
+ *
+ * @author geekidea
+ * @date 2022/03/15
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "openapi")
+public class OpenApiProperties {
+
+    /**
+     * 标题
+     */
+    private String title;
+
+    /**
+     * 描述
+     */
+    private String description;
+
+    /**
+     * 团队地址
+     */
+    private String termsOfService;
+
+    /**
+     * 联系人名称
+     */
+    private String contactName;
+
+    /**
+     * 联系人URL
+     */
+    private String contactUrl;
+
+    /**
+     * 联系人Email
+     */
+    private String contactEmail;
+
+    /**
+     * 版本
+     */
+    private String version;
+
+    /**
+     * 扩展描述
+     */
+    private String externalDescription;
+
+    /**
+     * 扩展Url
+     */
+    private String externalUrl;
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/OssProperties.java b/src/main/java/io/geekidea/boot/config/properties/OssProperties.java
new file mode 100644
index 00000000..def86438
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/OssProperties.java
@@ -0,0 +1,48 @@
+package io.geekidea.boot.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 阿里云OSS配置
+ *
+ * @author geekidea
+ * @date 2023/11/26
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "file.oss")
+public class OssProperties {
+
+    /**
+     * accessKeyId
+     */
+    private String accessKeyId;
+
+    /**
+     * accessKeySecret
+     */
+    private String accessKeySecret;
+
+    /**
+     * 访问OSS的域名
+     */
+    private String endpoint;
+
+    /**
+     * bucketName
+     */
+    private String bucketName;
+
+    /**
+     * 根目录
+     */
+    private String rootDir;
+
+    /**
+     * 访问域名
+     */
+    private String accessDomain;
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/WxMpProperties.java b/src/main/java/io/geekidea/boot/config/properties/WxMpProperties.java
new file mode 100644
index 00000000..20a398ec
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/WxMpProperties.java
@@ -0,0 +1,28 @@
+package io.geekidea.boot.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 微信小程序属性配置
+ *
+ * @author geekidea
+ * @date 2023/11/26
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "wx.mp")
+public class WxMpProperties {
+
+    /**
+     * appid
+     */
+    private String appid;
+
+    /**
+     * 密钥
+     */
+    private String secret;
+
+}
diff --git a/src/main/java/io/geekidea/boot/config/properties/XssProperties.java b/src/main/java/io/geekidea/boot/config/properties/XssProperties.java
new file mode 100644
index 00000000..90b407bb
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/config/properties/XssProperties.java
@@ -0,0 +1,36 @@
+package io.geekidea.boot.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author geekidea
+ * @date 2023/7/9
+ **/
+@Data
+@Component
+@ConfigurationProperties(prefix = "xss")
+public class XssProperties {
+
+    /**
+     * 是否启用
+     */
+    private boolean enable;
+
+    /**
+     * 过滤的路径
+     */
+    private String[] urlPatterns;
+
+    /**
+     * 排序
+     */
+    private int order;
+
+    /**
+     * 是否支持异步
+     */
+    private boolean async;
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/annotation/Log.java b/src/main/java/io/geekidea/boot/framework/annotation/Log.java
new file mode 100644
index 00000000..4a61d8d3
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/annotation/Log.java
@@ -0,0 +1,21 @@
+package io.geekidea.boot.framework.annotation;
+
+import io.geekidea.boot.common.enums.SysLogType;
+
+/**
+ * @author geekidea
+ * @date 2022/8/3
+ **/
+public @interface Log {
+
+    /**
+     * 描述
+     *
+     * @return
+     */
+    String value() default "";
+
+
+    SysLogType type() default SysLogType.OTHER;
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/aop/SysLogAop.java b/src/main/java/io/geekidea/boot/framework/aop/SysLogAop.java
new file mode 100644
index 00000000..364646cb
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/aop/SysLogAop.java
@@ -0,0 +1,616 @@
+package io.geekidea.boot.framework.aop;
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.http.useragent.Browser;
+import cn.hutool.http.useragent.Platform;
+import cn.hutool.http.useragent.UserAgent;
+import cn.hutool.http.useragent.UserAgentUtil;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.auth.util.CommonLoginUtil;
+import io.geekidea.boot.auth.util.TokenUtil;
+import io.geekidea.boot.common.constant.AopConstant;
+import io.geekidea.boot.common.constant.CommonConstant;
+import io.geekidea.boot.common.enums.SystemType;
+import io.geekidea.boot.config.properties.LogAopProperties;
+import io.geekidea.boot.framework.annotation.Log;
+import io.geekidea.boot.framework.enums.ResponseLogType;
+import io.geekidea.boot.framework.exception.GlobalExceptionHandler;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.framework.bean.IpRegion;
+import io.geekidea.boot.system.entity.SysLog;
+import io.geekidea.boot.system.mapper.SysLogMapper;
+import io.geekidea.boot.util.*;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.MDC;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.core.annotation.Order;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 日志AOP
+ *
+ * @author geekidea
+ * @date 2022/6/9
+ **/
+@Slf4j
+@Data
+@Aspect
+@Component
+@Order(1)
+@EnableConfigurationProperties(LogAopProperties.class)
+@ConditionalOnProperty(name = "log-aop.enable", havingValue = "true", matchIfMissing = true)
+public class SysLogAop {
+
+    private static final String USERNAME = "username";
+    private static final String REFERER = "Referer";
+    private static final String USER_AGENT = "User-Agent";
+    private static final String ORIGIN = "Origin";
+    private static final String SWAGGER_UI = "swagger-ui";
+    private static final String KNIFE4J = "Knife4j";
+    private static final String POSTMAN = "postman";
+    private static final String REQUEST_ORIGION = "Request-Origion";
+
+    /**
+     * 本地线程变量，保存操作日志到当前线程中
+     */
+    private static final ThreadLocal<SysLog> LOCAL_LOG = new ThreadLocal<>();
+
+    @Autowired
+    private LogAopProperties logAopProperties;
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    @Autowired
+    private SysLogMapper sysLogMapper;
+
+    @Around(AopConstant.PROJECT_POINTCUT)
+    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
+        HttpServletRequest request = HttpServletRequestUtil.getRequest();
+        // 请求路径
+        String servletPath = request.getServletPath();
+        // 是否处理日志
+        boolean isHandleLog = isHandleLog(servletPath);
+        if (!isHandleLog) {
+            // 如果是排除的路径，则直接跳过
+            return joinPoint.proceed();
+        }
+        Object result;
+        try {
+            // 执行方法之前处理
+            handleBefore(joinPoint, request, servletPath);
+            // 执行目标方法,获得返回值
+            result = joinPoint.proceed();
+            // 执行方法之后处理
+            handleAfter(result);
+        } catch (Throwable e) {
+            // 执行方法异常处理
+            handleException(e);
+            throw e;
+        } finally {
+            // 执行方法结束后
+            handleFinally();
+        }
+        return result;
+    }
+
+    /**
+     * 是否处理日志
+     *
+     * @param servletPath
+     * @return
+     */
+    private boolean isHandleLog(String servletPath) {
+        // 判断是否是排除路径
+        boolean isHandleLog = true;
+        // 排除路径
+        List<String> excludePaths = logAopProperties.getExcludePaths();
+        if (CollectionUtils.isNotEmpty(excludePaths)) {
+            for (String excludePath : excludePaths) {
+                AntPathMatcher antPathMatcher = new AntPathMatcher();
+                boolean match = antPathMatcher.match(excludePath, servletPath);
+                if (match) {
+                    isHandleLog = false;
+                    break;
+                }
+            }
+        }
+        return isHandleLog;
+    }
+
+    /**
+     * 执行方法之前
+     *
+     * @param joinPoint
+     * @param request
+     * @param servletPath
+     * @throws Exception
+     */
+    private void handleBefore(ProceedingJoinPoint joinPoint, HttpServletRequest request, String servletPath) throws Exception {
+        try {
+            Signature signature = joinPoint.getSignature();
+            MethodSignature methodSignature = (MethodSignature) signature;
+            Method method = methodSignature.getMethod();
+            // 系统日志
+            SysLog sysLog = new SysLog();
+            // 将日志保存到当前线程中
+            LOCAL_LOG.set(sysLog);
+            // 设置请求路径
+            sysLog.setRequestUrl(servletPath);
+            // 设置请求时间，包含毫秒
+            String requestTime = DateUtil.format(new Date(), DatePattern.NORM_DATETIME_MS_PATTERN);
+            sysLog.setRequestTime(requestTime);
+            // 设置日志链路ID
+            String traceId = MDC.get(CommonConstant.TRACE_ID);
+            sysLog.setTraceId(traceId);
+            // 设置IP区域信息
+            handleIpArea(sysLog);
+            // 设置内容类型
+            String contentType = request.getContentType();
+            sysLog.setContentType(contentType);
+            // 处理方法上的注解
+            handleAnnotation(method, sysLog);
+            // 请求方式，GET/POST
+            sysLog.setRequestMethod(request.getMethod());
+            // 处理请求参数
+            String requestBodyString = handleRequestParam(request, method, sysLog);
+            // 设置类名称
+            String className = method.getDeclaringClass().getName();
+            sysLog.setClassName(className);
+            // 处理模块名称
+            handleModuleName(method, sysLog);
+            // 设置方法名称
+            String methodName = method.getName();
+            sysLog.setMethodName(methodName);
+            // 来源地址
+            String referer = request.getHeader(REFERER);
+            sysLog.setReferer(referer);
+            // 处理用户代理信息
+            String userAgentString = handleUserAgent(request, sysLog);
+            // 用户来源
+            String origin = request.getHeader(ORIGIN);
+            if (StringUtils.isBlank(origin)) {
+                if (StringUtils.isNotBlank(referer) && referer.contains(SWAGGER_UI)) {
+                    sysLog.setOrigin(SWAGGER_UI);
+                }
+            } else {
+                sysLog.setOrigin(origin);
+            }
+            if (StringUtils.isNotBlank(userAgentString)) {
+                if (userAgentString.toLowerCase().contains(POSTMAN)) {
+                    sysLog.setOrigin(POSTMAN);
+                }
+            }
+            String requestOrigion = request.getHeader(REQUEST_ORIGION);
+            if (KNIFE4J.equals(requestOrigion)) {
+                sysLog.setSourceType(KNIFE4J);
+            }
+            // 处理登录人信息
+            handleLoginUser(sysLog, servletPath, requestBodyString);
+            StringBuffer requestURL = request.getRequestURL();
+            printLog("requestURL: " + requestURL);
+            // 打印请求头
+            printHeadLog(request);
+            // 打印请求日志
+            printLog(sysLog);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 执行方法之后
+     *
+     * @param result
+     */
+    private void handleAfter(Object result) {
+        try {
+            if (result != null) {
+                SysLog sysLog = LOCAL_LOG.get();
+                if (result instanceof ApiResult) {
+                    ApiResult apiResult = (ApiResult) result;
+                    boolean success = apiResult.isSuccess();
+                    sysLog.setResponseSuccess(success);
+                    sysLog.setResponseCode(apiResult.getCode());
+                    sysLog.setResponseMessage(apiResult.getMsg());
+                    Object responseData = apiResult.getData();
+                    if (responseData != null) {
+                        String responseDataString = JSON.toJSONString(responseData);
+                        sysLog.setResponseData(responseDataString);
+                    }
+                    // 响应日志类型
+                    ResponseLogType responseLogType = logAopProperties.getResponseLogType();
+                    if (ResponseLogType.FULL == responseLogType) {
+                        printLog("response：" + JSON.toJSONString(apiResult));
+                    } else if (ResponseLogType.PART == responseLogType) {
+                        printLog("response: code:" + apiResult.getCode() + ",success:" + apiResult.isSuccess() + ",msg:" + apiResult.getMsg());
+                    }
+                } else {
+                    printLog("response:" + result);
+                }
+            } else {
+                printLog("response:");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * 执行方法异常
+     *
+     * @param e
+     */
+    private void handleException(Throwable e) {
+        try {
+            ApiResult apiResult = GlobalExceptionHandler.handle(e);
+            SysLog sysLog = LOCAL_LOG.get();
+            sysLog.setResponseSuccess(false);
+            sysLog.setResponseCode(apiResult.getCode());
+            sysLog.setResponseMessage(apiResult.getMsg());
+            String exceptionClassName = e.getClass().getSimpleName();
+            sysLog.setExceptionName(exceptionClassName);
+            String exceptionMessage = e.getMessage();
+            sysLog.setExceptionMessage(exceptionMessage);
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    /**
+     * 执行方法结束后处理
+     */
+    private void handleFinally() {
+        try {
+            SysLog sysLog = LOCAL_LOG.get();
+            LOCAL_LOG.remove();
+            String requestTime = sysLog.getRequestTime();
+            Date requestDate = DateUtil.parse(requestTime, DatePattern.NORM_DATETIME_MS_PATTERN);
+            long startTime = requestDate.getTime();
+            Date responseDate = new Date();
+            long nowTime = responseDate.getTime();
+            long diffTime = nowTime - startTime;
+            String diffTimeDesc;
+            if (diffTime > CommonConstant.ONE_THOUSAND) {
+                BigDecimal second = NumberUtil.div(new BigDecimal(diffTime), CommonConstant.ONE_THOUSAND, 2);
+                diffTimeDesc = second + "s";
+            } else {
+                diffTimeDesc = diffTime + "ms";
+            }
+            sysLog.setDiffTimeDesc(diffTimeDesc);
+            String responseTime = DateUtil.format(responseDate, DatePattern.NORM_DATETIME_MS_PATTERN);
+            sysLog.setResponseTime(responseTime);
+            sysLog.setDiffTime(diffTime);
+            // 保存日志
+            saveSysLog(sysLog);
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+
+        }
+    }
+
+    /**
+     * isHandleLog
+     *
+     * @param request
+     * @param method
+     * @param sysLog
+     * @return
+     */
+    private String handleRequestParam(HttpServletRequest request, Method method, SysLog sysLog) {
+        // 判断是否是JSON参数请求
+        Annotation[][] annotations = method.getParameterAnnotations();
+        boolean isRequestBody = isRequestBody(annotations);
+        // 是否是JSON请求映射参数
+        sysLog.setIsRequestBody(isRequestBody);
+        String requestBodyString = handleRequestParam(request, sysLog, isRequestBody);
+        try {
+            // 设置token
+            String token = TokenUtil.getToken();
+            sysLog.setToken(token);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return requestBodyString;
+    }
+
+    /**
+     * 处理IP区域
+     *
+     * @param sysLog
+     */
+    private void handleIpArea(SysLog sysLog) {
+        // 设置IP
+        String ip = IpUtil.getRequestIp();
+        sysLog.setRequestIp(ip);
+        try {
+            // 设置IP归属地信息
+            IpRegion ipRegion = IpRegionUtil.getIpRegion(ip);
+            if (ipRegion != null) {
+                sysLog.setIpCountry(ipRegion.getCountry());
+                sysLog.setIpProvince(ipRegion.getProvince());
+                sysLog.setIpCity(ipRegion.getCity());
+                sysLog.setIpAreaDesc(ipRegion.getAreaDesc());
+                sysLog.setIpIsp(ipRegion.getIsp());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 处理方法注解
+     *
+     * @param method
+     * @param sysLog
+     */
+    private void handleAnnotation(Method method, SysLog sysLog) {
+        // 日志名称 获取@Operation的value
+        Operation operation = method.getAnnotation(Operation.class);
+        if (operation != null) {
+            String summary = operation.summary();
+            sysLog.setLogName(summary);
+        }
+        // 日志类型 获取@Log的code
+        Log log = method.getAnnotation(Log.class);
+        if (log != null) {
+            Integer logType = log.type().getCode();
+            sysLog.setLogType(logType);
+        }
+        // 权限编码 获取@Permission的value
+        Permission permission = method.getAnnotation(Permission.class);
+        if (permission != null) {
+            String permissionCode = permission.value();
+            sysLog.setPermissionCode(permissionCode);
+        }
+    }
+
+    /**
+     * 处理模块名称
+     *
+     * @param method
+     * @param sysLog
+     */
+    private void handleModuleName(Method method, SysLog sysLog) {
+        // 模块名称
+        try {
+            String packName = method.getDeclaringClass().getPackage().getName();
+            int lastIndexOf = packName.lastIndexOf(".");
+            packName = packName.substring(0, lastIndexOf);
+            String moduleName = packName.substring(packName.lastIndexOf(".") + 1);
+            sysLog.setModuleName(moduleName);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 处理用户代理环境
+     *
+     * @param request
+     * @param sysLog
+     * @return
+     */
+    private String handleUserAgent(HttpServletRequest request, SysLog sysLog) {
+        // 用户环境
+        String userAgentString = request.getHeader(USER_AGENT);
+        sysLog.setUserAgent(userAgentString);
+        UserAgent userAgent;
+        try {
+            userAgent = UserAgentUtil.parse(userAgentString);
+            if (userAgent != null) {
+                // 是否是手机
+                boolean isMobile = userAgent.isMobile();
+                sysLog.setIsMobile(isMobile);
+                // 操作系统平台名称
+                Platform platform = userAgent.getPlatform();
+                String platformName = platform.getName();
+                sysLog.setPlatformName(platformName);
+                // 浏览器名称
+                Browser browser = userAgent.getBrowser();
+                String browserName = browser.getName();
+                sysLog.setBrowserName(browserName);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return userAgentString;
+    }
+
+    private String handleRequestParam(HttpServletRequest request, SysLog sysLog, boolean isRequestBody) {
+        String requestBodyString = null;
+        String parameterMapString = null;
+        if (isRequestBody) {
+            requestBodyString = (String) request.getAttribute(CommonConstant.REQUEST_PARAM_BODY_STRING);
+        }
+        Map<String, String[]> paramsMap = request.getParameterMap();
+        if (MapUtils.isNotEmpty(paramsMap)) {
+            JSONObject paramObject = getJsonForParamMap(paramsMap);
+            parameterMapString = JSON.toJSONString(paramObject);
+        }
+        String requestParam = null;
+        if (StringUtils.isNotBlank(requestBodyString) && StringUtils.isNotBlank(parameterMapString)) {
+            requestParam = "paramMap:" + parameterMapString + ",requestBody:" + requestBodyString;
+        } else if (StringUtils.isNotBlank(requestBodyString)) {
+            requestParam = requestBodyString;
+        } else if (StringUtils.isNotBlank(parameterMapString)) {
+            requestParam = parameterMapString;
+        }
+        // 请求参数
+        sysLog.setRequestParam(requestParam);
+        return requestBodyString;
+    }
+
+    /**
+     * 打印请求头
+     *
+     * @param request
+     */
+    private void printHeadLog(HttpServletRequest request) {
+        if (logAopProperties.isPrintHeadLog()) {
+            Enumeration<String> headerNames = request.getHeaderNames();
+            while (headerNames.hasMoreElements()) {
+                String headName = headerNames.nextElement();
+                String headerValue = request.getHeader(headName);
+                printLog(headName + "：" + headerValue);
+            }
+        }
+    }
+
+    /**
+     * 处理登录人信息
+     *
+     * @param sysLog
+     * @param servletPath
+     * @param requestBodyString
+     * @throws Exception
+     */
+    private void handleLoginUser(SysLog sysLog, String servletPath, String requestBodyString) throws Exception {
+        try {
+            // TODO 配置是否打印响应日志，通常增删改才需要记录响应日志，查询不用记录
+            String token = sysLog.getToken();
+            if (StringUtils.isNotBlank(token)) {
+                SystemType systemType = SystemTypeUtil.getSystemTypeByToken(token);
+                if (systemType != null) {
+                    // 系统类型 1：Admin管理后台，2：APP移动端端
+                    sysLog.setSystemType(systemType.getCode());
+                    // 记录用户ID
+                    try {
+                        Long userId = CommonLoginUtil.getUserId(systemType);
+                        sysLog.setUserId(userId);
+                    } catch (Exception e) {
+
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 异步保存操作日志
+     *
+     * @param sysLog
+     */
+    @Async
+    public void saveSysLog(SysLog sysLog) {
+        try {
+            if (sysLog != null) {
+                Integer logType = sysLog.getLogType();
+                if (logType != null) {
+                    int result = sysLogMapper.insert(sysLog);
+                    if (result != 1) {
+                        log.error("保存操作日志错误" + sysLog);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 打印日志
+     *
+     * @param sysLog
+     */
+    private void printLog(SysLog sysLog) {
+        if (logAopProperties.isPrintLog()) {
+            if (sysLog != null) {
+                String requestLog = "request: " + sysLog.getRequestMethod() + "," + sysLog.getRequestUrl() + "," + sysLog.getContentType() + ",isRequestBody:" + sysLog.getIsRequestBody() + "," + sysLog.getRequestParam();
+                printLog(requestLog);
+            }
+        }
+    }
+
+    /**
+     * 打印日志
+     *
+     * @param msg
+     */
+    private void printLog(String msg) {
+        if (logAopProperties.isPrintLog()) {
+            log.info(msg);
+        }
+    }
+
+    /**
+     * 是否是json参数请求
+     *
+     * @param annotationArrays
+     * @return
+     */
+    private boolean isRequestBody(Annotation[][] annotationArrays) {
+        if (annotationArrays == null) {
+            return false;
+        }
+        for (Annotation[] annotationArray : annotationArrays) {
+            if (ArrayUtils.isNotEmpty(annotationArray)) {
+                for (Annotation annotation : annotationArray) {
+                    if (annotation instanceof RequestBody) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 转换表单参数为json对象
+     *
+     * @param paramsMap
+     * @return
+     */
+    private JSONObject getJsonForParamMap(Map<String, String[]> paramsMap) {
+        JSONObject jsonObject = new JSONObject();
+        for (Map.Entry<String, String[]> kv : paramsMap.entrySet()) {
+            String key = kv.getKey();
+            String[] values = kv.getValue();
+            if (values == null) {
+                // 没有值
+                jsonObject.put(key, null);
+            } else if (values.length == 1) {
+                // 一个值
+                jsonObject.put(key, values[0]);
+            } else { // 多个值
+                jsonObject.put(key, values);
+            }
+        }
+        return jsonObject;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/bean/Cpu.java b/src/main/java/io/geekidea/boot/framework/bean/Cpu.java
new file mode 100644
index 00000000..12285e5d
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/bean/Cpu.java
@@ -0,0 +1,36 @@
+package io.geekidea.boot.framework.bean;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * CPU信息
+ *
+ * @author geekidea
+ * @since 2023-12-16
+ */
+@Data
+@Schema(description = "CPU信息")
+public class Cpu implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "CPU核心数")
+    private Integer cpuNum;
+
+    @Schema(description = "总CPU使用率：获取用户+系统的总的CPU使用率")
+    private double used;
+
+    @Schema(description = "CPU系统使用率")
+    private double sys;
+
+    @Schema(description = "CPU用户使用率")
+    private double user;
+
+    @Schema(description = "CPU当前空闲率")
+    private double free;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/framework/bean/Disk.java b/src/main/java/io/geekidea/boot/framework/bean/Disk.java
new file mode 100644
index 00000000..fd2f8299
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/bean/Disk.java
@@ -0,0 +1,55 @@
+package io.geekidea.boot.framework.bean;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 磁盘信息
+ *
+ * @author geekidea
+ * @since 2023-12-17
+ */
+@Data
+@Schema(description = "磁盘信息")
+public class Disk implements Serializable {
+
+    private static final long serialVersionUID = -5695612481481869927L;
+
+    @Schema(description = "磁盘路径")
+    private String mount;
+
+    @Schema(description = "文件系统类型")
+    private String type;
+
+    @Schema(description = "磁盘名称")
+    private String name;
+
+    @Schema(description = "磁盘总大小")
+    private long totalSpace;
+
+    @Schema(description = "磁盘可用空间大小")
+    private long usableSpace;
+
+    @Schema(description = "磁盘已使用空间大小")
+    private long usedSpace;
+
+    @Schema(description = "磁盘总大小带单位")
+    private String totalSpaceUnit;
+
+    @Schema(description = "磁盘总大小带单位")
+    private String usableSpaceUnit;
+
+    @Schema(description = "磁盘总大小带单位")
+    private String usedSpaceSpaceUnit;
+
+    @Schema(description = "可用百分比")
+    private BigDecimal usableRate;
+
+    @Schema(description = "已用百分比")
+    private BigDecimal usedRate;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/framework/bean/IpRegion.java b/src/main/java/io/geekidea/boot/framework/bean/IpRegion.java
new file mode 100644
index 00000000..c1e62839
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/bean/IpRegion.java
@@ -0,0 +1,37 @@
+package io.geekidea.boot.framework.bean;
+
+import lombok.Data;
+
+/**
+ * @author geekidea
+ * @date 2023/6/23
+ **/
+@Data
+public class IpRegion {
+
+    /**
+     * 国家
+     */
+    private String country;
+
+    /**
+     * 省份
+     */
+    private String province;
+
+    /**
+     * 城市
+     */
+    private String city;
+
+    /**
+     * 区域描述
+     */
+    private String areaDesc;
+
+    /**
+     * 运营商
+     */
+    private String isp;
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/bean/Jvm.java b/src/main/java/io/geekidea/boot/framework/bean/Jvm.java
new file mode 100644
index 00000000..41894994
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/bean/Jvm.java
@@ -0,0 +1,73 @@
+package io.geekidea.boot.framework.bean;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * JVM信息
+ *
+ * @author geekidea
+ * @date 2023/12/16
+ **/
+@Data
+@Schema(description = "JVM信息")
+public class Jvm implements Serializable {
+
+    private static final long serialVersionUID = 7303907109495309018L;
+
+    @Schema(description = "JVM已分配内存")
+    private long totalMemory;
+
+    @Schema(description = "JVM最大内存")
+    private long maxMemory;
+
+    @Schema(description = "JVM最大可用内存")
+    private long usableMemory;
+
+    @Schema(description = "JVM空闲内存")
+    private long freeMemory;
+
+    @Schema(description = "JVM已分配内存存带单位")
+    private String totalMemoryUnit;
+
+    @Schema(description = "JVM最大内存带单位")
+    private String maxMemoryUnit;
+
+    @Schema(description = "JVM最大可用内存带单位")
+    private String usableMemoryUnit;
+
+    @Schema(description = "JVM空闲内存带单位")
+    private String freeMemoryUnit;
+
+    @Schema(description = "JVM名称")
+    private String name;
+
+    @Schema(description = "JVM厂商")
+    private String vendor;
+
+    @Schema(description = "java版本")
+    private String version;
+
+    @Schema(description = "java按照目录")
+    private String home;
+
+    @Schema(description = "进程ID")
+    private int processId;
+
+    @Schema(description = "服务启动的开始时间")
+    private Date startTime;
+
+    @Schema(description = "服务已运行的时间")
+    private long userTime;
+
+    @Schema(description = "服务已运行的时间描述")
+    private String userTimeDesc;
+
+    @Schema(description = "进程CPU使用率")
+    private BigDecimal processCpuRate;
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/bean/Memory.java b/src/main/java/io/geekidea/boot/framework/bean/Memory.java
new file mode 100644
index 00000000..0bb2a95f
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/bean/Memory.java
@@ -0,0 +1,45 @@
+package io.geekidea.boot.framework.bean;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 内存信息
+ *
+ * @author geekidea
+ * @date 2023/12/16
+ **/
+@Data
+@Schema(description = "内存信息")
+public class Memory implements Serializable {
+
+    private static final long serialVersionUID = -7825560455963508475L;
+
+    @Schema(description = "总内存大小")
+    private long total;
+
+    @Schema(description = "空闲内存大小")
+    private long available;
+
+    @Schema(description = "已使用内存大小")
+    private long used;
+
+    @Schema(description = "总内存大小带单位")
+    private String totalUnit;
+
+    @Schema(description = "空闲内存大小带单位")
+    private String availableUnit;
+
+    @Schema(description = "已使用内存大小带单位")
+    private String usedUnit;
+
+    @Schema(description = "使用率")
+    private BigDecimal usedRate;
+
+    @Schema(description = "空闲率")
+    private BigDecimal availableRate;
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/bean/OperatingSystemInfo.java b/src/main/java/io/geekidea/boot/framework/bean/OperatingSystemInfo.java
new file mode 100644
index 00000000..ca2e569a
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/bean/OperatingSystemInfo.java
@@ -0,0 +1,30 @@
+package io.geekidea.boot.framework.bean;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 操作系统信息
+ * @author geekidea
+ * @date 2023/12/17
+ **/
+@Data
+@Schema(description = "操作系统信息")
+public class OperatingSystemInfo implements Serializable {
+
+    private static final long serialVersionUID = 8809514536353797869L;
+
+    @Schema(description = "系统名称")
+    private String name;
+
+    @Schema(description = "系统架构")
+    private String arch;
+
+    @Schema(description = "系统版本")
+    private String version;
+
+    @Schema(description = "IP地址")
+   private String address;
+}
diff --git a/src/main/java/io/geekidea/boot/framework/bean/ProjectInfo.java b/src/main/java/io/geekidea/boot/framework/bean/ProjectInfo.java
new file mode 100644
index 00000000..292402f4
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/bean/ProjectInfo.java
@@ -0,0 +1,36 @@
+package io.geekidea.boot.framework.bean;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 项目信息
+ *
+ * @author geekidea
+ * @date 2023/12/17
+ **/
+@Data
+@Schema(description = "项目信息")
+public class ProjectInfo implements Serializable {
+
+    private static final long serialVersionUID = 8809514536353797869L;
+
+    @Schema(description = "项目名称")
+    private String name;
+
+    @Schema(description = "项目端口")
+    private String port;
+
+    @Schema(description = "项目路径")
+    private String contextPath;
+
+    @Schema(description = "激活的环境")
+    private String active;
+
+    @Schema(description = "项目运行目录")
+    private String userDir;
+
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/bean/ServerInfo.java b/src/main/java/io/geekidea/boot/framework/bean/ServerInfo.java
new file mode 100644
index 00000000..cc5d2eb6
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/bean/ServerInfo.java
@@ -0,0 +1,41 @@
+package io.geekidea.boot.framework.bean;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 服务信息
+ *
+ * @author geekidea
+ * @since 2023-12-16
+ */
+@Data
+@Schema(description = "服务信息")
+public class ServerInfo implements Serializable {
+
+    private static final long serialVersionUID = 3621691988717747918L;
+
+    @Schema(description = "CPU信息")
+    private Cpu cpu;
+
+    @Schema(description = "内存信息")
+    private Memory memory;
+
+    @Schema(description = "JVM信息")
+    private Jvm jvm;
+
+    @Schema(description = "磁盘列表")
+    private List<Disk> diskList;
+
+    @Schema(description = "操作系统信息")
+    private OperatingSystemInfo operatingSystemInfo;
+
+    @Schema(description = "项目信息")
+    private ProjectInfo projectInfo;
+
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/framework/enums/ResponseLogType.java b/src/main/java/io/geekidea/boot/framework/enums/ResponseLogType.java
new file mode 100644
index 00000000..bdab62fb
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/enums/ResponseLogType.java
@@ -0,0 +1,63 @@
+package io.geekidea.boot.framework.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author geekidea
+ * @date 2024/2/12
+ **/
+public enum ResponseLogType {
+
+    FULL(1, "全部"),
+    PART(2, "部分"),
+    NONE(3, "无");
+
+    private Integer code;
+    private String desc;
+
+    ResponseLogType(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, ResponseLogType> map = new HashMap<>();
+
+    static {
+        for (ResponseLogType type : values()) {
+            map.put(type.code, type);
+        }
+    }
+
+    public static Integer getCode(ResponseLogType type) {
+        if (type == null) {
+            return PART.code;
+        }
+        return type.code;
+    }
+
+    public static ResponseLogType get(Integer code) {
+        ResponseLogType type = map.get(code);
+        if (type == null) {
+            return PART;
+        }
+        return type;
+    }
+
+    public static String getDesc(Integer code) {
+        ResponseLogType type = get(code);
+        if (type == null) {
+            return null;
+        }
+        return type.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/exception/AuthException.java b/src/main/java/io/geekidea/boot/framework/exception/AuthException.java
new file mode 100644
index 00000000..83f42d1e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/exception/AuthException.java
@@ -0,0 +1,15 @@
+package io.geekidea.boot.framework.exception;
+
+/**
+ * 权限异常
+ *
+ * @author geekidea
+ * @date 2018-11-08
+ */
+public class AuthException extends BusinessException {
+
+    public AuthException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/exception/BusinessException.java b/src/main/java/io/geekidea/boot/framework/exception/BusinessException.java
new file mode 100644
index 00000000..f26053ca
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/exception/BusinessException.java
@@ -0,0 +1,15 @@
+package io.geekidea.boot.framework.exception;
+
+/**
+ * 业务异常
+ *
+ * @author geekidea
+ * @date 2018-11-08
+ */
+public class BusinessException extends RuntimeException {
+
+    public BusinessException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/exception/GlobalExceptionHandler.java b/src/main/java/io/geekidea/boot/framework/exception/GlobalExceptionHandler.java
new file mode 100644
index 00000000..868689f3
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/exception/GlobalExceptionHandler.java
@@ -0,0 +1,78 @@
+package io.geekidea.boot.framework.exception;
+
+import io.geekidea.boot.framework.response.ApiCode;
+import io.geekidea.boot.framework.response.ApiResult;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
+
+import java.util.List;
+
+/**
+ * 全局异常处理
+ *
+ * @author geekidea
+ * @date 2021-4-13
+ */
+@Slf4j
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    /**
+     * 全局异常处理
+     *
+     * @param exception
+     * @return
+     */
+    public static ApiResult handle(Throwable exception) {
+        if (exception instanceof LoginException) {
+            return ApiResult.fail(ApiCode.TOKEN_EXCEPTION, exception.getMessage());
+        } else if (exception instanceof NotAuthException) {
+            return ApiResult.fail(exception.getMessage());
+        } else if (exception instanceof BusinessException) {
+            return ApiResult.fail(exception.getMessage());
+        } else if (exception instanceof MethodArgumentNotValidException) {
+            MethodArgumentNotValidException ex = (MethodArgumentNotValidException) exception;
+            BindingResult bindingResult = ex.getBindingResult();
+            List<FieldError> fieldErrors = bindingResult.getFieldErrors();
+            FieldError fieldError = fieldErrors.get(0);
+            String errorMessage = fieldError.getDefaultMessage();
+            log.error("参数校验错误" + "：" + errorMessage);
+            return ApiResult.fail(errorMessage);
+        } else if (exception instanceof HttpMessageNotReadableException) {
+            return ApiResult.fail("请求参数解析异常");
+        } else if (exception instanceof MethodArgumentTypeMismatchException) {
+            return ApiResult.fail("请求参数数据类型错误");
+        } else if (exception instanceof DuplicateKeyException) {
+            return ApiResult.fail("数据违反唯一约束");
+        } else if (exception instanceof DataIntegrityViolationException) {
+            return ApiResult.fail("数据完整性异常");
+        } else if (exception instanceof HttpRequestMethodNotSupportedException) {
+            return ApiResult.fail(exception.getMessage());
+        }
+        return ApiResult.fail();
+    }
+
+    /**
+     * 全局异常处理
+     *
+     * @param exception
+     * @return
+     */
+    @ExceptionHandler(value = Exception.class)
+    @ResponseStatus(HttpStatus.OK)
+    public ApiResult exceptionHandle(Exception exception) {
+        log.error("exception:", exception);
+        return handle(exception);
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/framework/exception/LoginException.java b/src/main/java/io/geekidea/boot/framework/exception/LoginException.java
new file mode 100644
index 00000000..da7663dd
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/exception/LoginException.java
@@ -0,0 +1,15 @@
+package io.geekidea.boot.framework.exception;
+
+/**
+ * 登录异常
+ *
+ * @author geekidea
+ * @date 2018-11-08
+ */
+public class LoginException extends RuntimeException {
+
+    public LoginException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/exception/LoginTokenException.java b/src/main/java/io/geekidea/boot/framework/exception/LoginTokenException.java
new file mode 100644
index 00000000..e1b8fe81
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/exception/LoginTokenException.java
@@ -0,0 +1,15 @@
+package io.geekidea.boot.framework.exception;
+
+/**
+ * 登录token异常
+ *
+ * @author geekidea
+ * @date 2023-06-23
+ */
+public class LoginTokenException extends LoginException {
+
+    public LoginTokenException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/exception/NotAuthException.java b/src/main/java/io/geekidea/boot/framework/exception/NotAuthException.java
new file mode 100644
index 00000000..c4213537
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/exception/NotAuthException.java
@@ -0,0 +1,15 @@
+package io.geekidea.boot.framework.exception;
+
+/**
+ * 没有权限异常
+ *
+ * @author geekidea
+ * @date 2018-11-08
+ */
+public class NotAuthException extends BusinessException {
+
+    public NotAuthException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/exception/UploadException.java b/src/main/java/io/geekidea/boot/framework/exception/UploadException.java
new file mode 100644
index 00000000..63260c26
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/exception/UploadException.java
@@ -0,0 +1,15 @@
+package io.geekidea.boot.framework.exception;
+
+/**
+ * 文件上传异常
+ *
+ * @author geekidea
+ * @date 2023-11-26
+ */
+public class UploadException extends BusinessException {
+
+    public UploadException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/filter/JsonHttpServletRequestWrapper.java b/src/main/java/io/geekidea/boot/framework/filter/JsonHttpServletRequestWrapper.java
new file mode 100644
index 00000000..8438011c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/filter/JsonHttpServletRequestWrapper.java
@@ -0,0 +1,78 @@
+package io.geekidea.boot.framework.filter;
+
+import org.springframework.util.StreamUtils;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * @author geekidea
+ * @date 2021/12/19
+ **/
+public class JsonHttpServletRequestWrapper extends HttpServletRequestWrapper {
+    private byte[] body;
+
+    public JsonHttpServletRequestWrapper(HttpServletRequest request) {
+        super(request);
+        //保存一份InputStream，将其转换为字节数组
+        try {
+            body = StreamUtils.copyToByteArray(request.getInputStream());
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 转换成String
+     *
+     * @return
+     */
+    public String getBodyString() {
+        String bodyString = new String(body, StandardCharsets.UTF_8);
+        return bodyString;
+    }
+
+    @Override
+    public BufferedReader getReader() throws IOException {
+        return new BufferedReader(new InputStreamReader(getInputStream()));
+    }
+
+    /**
+     * 把保存好的InputStream，传下去
+     *
+     * @return
+     * @throws IOException
+     */
+    @Override
+    public ServletInputStream getInputStream() throws IOException {
+        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
+        return new ServletInputStream() {
+            @Override
+            public int read() throws IOException {
+                return bais.read();
+            }
+
+            @Override
+            public boolean isFinished() {
+                return false;
+            }
+
+            @Override
+            public boolean isReady() {
+                return false;
+            }
+
+            @Override
+            public void setReadListener(ReadListener listener) {
+
+            }
+        };
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/framework/filter/JsonRequestBodyFilter.java b/src/main/java/io/geekidea/boot/framework/filter/JsonRequestBodyFilter.java
new file mode 100644
index 00000000..e7cc72fe
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/filter/JsonRequestBodyFilter.java
@@ -0,0 +1,48 @@
+package io.geekidea.boot.framework.filter;
+
+import io.geekidea.boot.common.constant.CommonConstant;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * @author geekidea
+ * @date 2022/3/22
+ **/
+@Slf4j
+public class JsonRequestBodyFilter implements Filter {
+
+    private static final String APPLICATION_JSON = "application/json";
+    private static final String METHOD_POST = "POST";
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        JsonHttpServletRequestWrapper requestWrapper = null;
+        try {
+            if (request instanceof HttpServletRequest) {
+                HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+                String method = httpServletRequest.getMethod();
+                String contentType = httpServletRequest.getContentType();
+                if (METHOD_POST.equalsIgnoreCase(method) && StringUtils.isNotBlank(contentType)) {
+                    contentType = contentType.toLowerCase();
+                    if (contentType.startsWith(APPLICATION_JSON)) {
+                        requestWrapper = new JsonHttpServletRequestWrapper((HttpServletRequest) request);
+                        String bodyString = requestWrapper.getBodyString();
+                        requestWrapper.setAttribute(CommonConstant.REQUEST_PARAM_BODY_STRING, bodyString);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        if (requestWrapper == null) {
+            chain.doFilter(request, response);
+        } else {
+            chain.doFilter(requestWrapper, response);
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/filter/TraceIdLogFilter.java b/src/main/java/io/geekidea/boot/framework/filter/TraceIdLogFilter.java
new file mode 100644
index 00000000..67e88ce7
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/filter/TraceIdLogFilter.java
@@ -0,0 +1,46 @@
+package io.geekidea.boot.framework.filter;
+
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import io.geekidea.boot.common.constant.CommonConstant;
+import io.geekidea.boot.util.IpUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.MDC;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * @author geekidea
+ * @date 2023/6/22
+ **/
+@Slf4j
+public class TraceIdLogFilter implements Filter {
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        try {
+            HttpServletRequest request = (HttpServletRequest) servletRequest;
+            // 设置日志链路ID
+            String traceId = IdWorker.getIdStr();
+            // 设置请求IP
+            String ip = IpUtil.getRequestIp(request);
+            try {
+                MDC.put(CommonConstant.TRACE_ID, traceId);
+                MDC.put(CommonConstant.IP, ip);
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            }
+            // 执行
+            filterChain.doFilter(servletRequest, servletResponse);
+        } catch (Exception e) {
+            throw e;
+        } finally {
+            // 移除日志链路ID
+            try {
+                MDC.remove(CommonConstant.TRACE_ID);
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/framework/handler/GlobalStringTrimHandler.java b/src/main/java/io/geekidea/boot/framework/handler/GlobalStringTrimHandler.java
new file mode 100644
index 00000000..94dba3d8
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/handler/GlobalStringTrimHandler.java
@@ -0,0 +1,22 @@
+package io.geekidea.boot.framework.handler;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.propertyeditors.StringTrimmerEditor;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.InitBinder;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * @author geekidea
+ * @date 2022/4/13
+ **/
+@Slf4j
+@RestControllerAdvice
+public class GlobalStringTrimHandler {
+
+    @InitBinder
+    public void initBinder(WebDataBinder webDataBinder) {
+        // 去掉表单请求参数中字符串的前后空格
+        webDataBinder.registerCustomEditor(String.class, new StringTrimmerEditor(false));
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/framework/interceptor/BaseExcludeMethodInterceptor.java b/src/main/java/io/geekidea/boot/framework/interceptor/BaseExcludeMethodInterceptor.java
new file mode 100644
index 00000000..98a04023
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/interceptor/BaseExcludeMethodInterceptor.java
@@ -0,0 +1,29 @@
+package io.geekidea.boot.framework.interceptor;
+
+import io.geekidea.boot.common.constant.CommonConstant;
+import org.springframework.web.method.HandlerMethod;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author geekidea
+ * @date 2023/12/3
+ **/
+public abstract class BaseExcludeMethodInterceptor extends BaseMethodInterceptor {
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        if (handler instanceof HandlerMethod) {
+            HandlerMethod handlerMethod = (HandlerMethod) handler;
+            // 如果是排除路径，则跳过处理
+            Boolean isExcludePath = (Boolean) request.getAttribute(CommonConstant.REQUEST_PARAM_EXCLUDE_PATH);
+            if (isExcludePath != null && isExcludePath) {
+                return true;
+            }
+            return preHandleMethod(request, response, handlerMethod);
+        }
+        return true;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/interceptor/BaseMethodInterceptor.java b/src/main/java/io/geekidea/boot/framework/interceptor/BaseMethodInterceptor.java
new file mode 100644
index 00000000..b8652d2c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/interceptor/BaseMethodInterceptor.java
@@ -0,0 +1,140 @@
+package io.geekidea.boot.framework.interceptor;
+
+import io.geekidea.boot.auth.annotation.AppUserRole;
+import io.geekidea.boot.auth.annotation.IgnoreLogin;
+import io.geekidea.boot.auth.annotation.Login;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2023/12/3
+ **/
+public abstract class BaseMethodInterceptor implements HandlerInterceptor {
+
+    /**
+     * 只处理方法的控制器
+     *
+     * @param request
+     * @param response
+     * @param handlerMethod
+     * @return
+     * @throws Exception
+     */
+    protected abstract boolean preHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        if (handler instanceof HandlerMethod) {
+            HandlerMethod handlerMethod = (HandlerMethod) handler;
+            return preHandleMethod(request, response, handlerMethod);
+        }
+        return true;
+    }
+
+    /**
+     * 判断方法上和类上是否存在@Login注解
+     *
+     * @param handlerMethod
+     * @return
+     */
+    protected boolean existsLoginAnnotation(HandlerMethod handlerMethod) {
+        // 从方法上获取登录注解
+        Login login = handlerMethod.getMethodAnnotation(Login.class);
+        if (login != null) {
+            return true;
+        }
+        // 从类上获取登录注解
+        login = handlerMethod.getMethod().getDeclaringClass().getAnnotation(Login.class);
+        if (login != null) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 判断方法上和类上是否存在@IgnoreLogin注解
+     *
+     * @param handlerMethod
+     * @return
+     */
+    protected boolean existsIgnoreLoginAnnotation(HandlerMethod handlerMethod) {
+        IgnoreLogin ignoreLogin = handlerMethod.getMethodAnnotation(IgnoreLogin.class);
+        if (ignoreLogin != null) {
+            return true;
+        }
+        ignoreLogin = handlerMethod.getMethod().getDeclaringClass().getAnnotation(IgnoreLogin.class);
+        if (ignoreLogin != null) {
+            return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * 获取方法上和类上的@AppUserRole注解
+     *
+     * @param handlerMethod
+     * @return
+     */
+    protected AppUserRole getAppUserRoleAnnotation(HandlerMethod handlerMethod) {
+        AppUserRole appUserRole = handlerMethod.getMethodAnnotation(AppUserRole.class);
+        if (appUserRole != null) {
+            return appUserRole;
+        }
+        appUserRole = handlerMethod.getMethod().getDeclaringClass().getAnnotation(AppUserRole.class);
+        if (appUserRole != null) {
+            return appUserRole;
+        }
+        return null;
+    }
+
+    /**
+     * 判断当前请求的路径，是否是不需要的路径
+     *
+     * @param request
+     * @param ignoreLoginPaths
+     * @return
+     */
+    protected boolean isIgnoreLoginPath(HttpServletRequest request, List<String> ignoreLoginPaths) {
+        String servletPath = request.getServletPath();
+        if (CollectionUtils.isNotEmpty(ignoreLoginPaths)) {
+            for (String ignoreLoginPath : ignoreLoginPaths) {
+                AntPathMatcher antPathMatcher = new AntPathMatcher();
+                boolean match = antPathMatcher.match(ignoreLoginPath, servletPath);
+                if (match) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断当前请求的路径，是否是需要登录的路径
+     *
+     * @param request
+     * @param loginPaths
+     * @return
+     */
+    protected boolean isCheckLoginPath(HttpServletRequest request, List<String> loginPaths) {
+        String servletPath = request.getServletPath();
+        if (CollectionUtils.isNotEmpty(loginPaths)) {
+            for (String loginPath : loginPaths) {
+                AntPathMatcher antPathMatcher = new AntPathMatcher();
+                boolean match = antPathMatcher.match(loginPath, servletPath);
+                if (match) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/interceptor/PageHelperClearInterceptor.java b/src/main/java/io/geekidea/boot/framework/interceptor/PageHelperClearInterceptor.java
new file mode 100644
index 00000000..ef723b86
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/interceptor/PageHelperClearInterceptor.java
@@ -0,0 +1,25 @@
+package io.geekidea.boot.framework.interceptor;
+
+import com.github.pagehelper.PageHelper;
+import org.springframework.web.method.HandlerMethod;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author geekidea
+ * @date 2022/10/12
+ **/
+public class PageHelperClearInterceptor extends BaseMethodInterceptor {
+
+    @Override
+    protected boolean preHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
+        try {
+            PageHelper.clearPage();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return true;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/jackson/deserializer/JacksonDateDeserializer.java b/src/main/java/io/geekidea/boot/framework/jackson/deserializer/JacksonDateDeserializer.java
new file mode 100644
index 00000000..958d28a2
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/jackson/deserializer/JacksonDateDeserializer.java
@@ -0,0 +1,59 @@
+package io.geekidea.boot.framework.jackson.deserializer;
+
+import com.fasterxml.jackson.core.JacksonException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author geekidea
+ * @date 2023/6/20
+ **/
+public class JacksonDateDeserializer extends JsonDeserializer<Date> {
+
+    public static final JacksonDateDeserializer INSTANCE = new JacksonDateDeserializer();
+
+    /**
+     * 日期格式数组
+     */
+    private static final String[] DATE_PATTERNS = {
+            "yyyy-MM-dd HH:mm:ss",
+            "yyyy-MM-dd",
+    };
+
+    @Override
+    public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
+        String dateString = jsonParser.getText();
+        if (dateString == null) {
+            return null;
+        }
+        dateString = dateString.trim();
+        if (StringUtils.isBlank(dateString)) {
+            return null;
+        }
+        Date date = null;
+        boolean flag = false;
+        for (int i = 0; i < DATE_PATTERNS.length; i++) {
+            try {
+                String datePattern = DATE_PATTERNS[i];
+                SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
+                date = simpleDateFormat.parse(dateString);
+                flag = true;
+                break;
+            } catch (ParseException e) {
+            }
+        }
+        if (flag) {
+            return date;
+        } else {
+            throw new IllegalArgumentException("不能解析的日期:" + dateString);
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/jackson/deserializer/JacksonStringDeserializer.java b/src/main/java/io/geekidea/boot/framework/jackson/deserializer/JacksonStringDeserializer.java
new file mode 100644
index 00000000..6c3a060c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/jackson/deserializer/JacksonStringDeserializer.java
@@ -0,0 +1,29 @@
+package io.geekidea.boot.framework.jackson.deserializer;
+
+import com.fasterxml.jackson.core.JacksonException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+
+import java.io.IOException;
+
+/**
+ * 处理json请求参数
+ *
+ * @author geekidea
+ * @date 2021-12-26
+ */
+public class JacksonStringDeserializer extends JsonDeserializer<String> {
+
+    public static final JacksonStringDeserializer INSTANCE = new JacksonStringDeserializer();
+
+    @Override
+    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
+        String value = jsonParser.getValueAsString();
+        if (value != null) {
+            // 去除字符串空格
+            value = value.trim();
+        }
+        return value;
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/framework/jackson/serializer/JacksonStringSerializer.java b/src/main/java/io/geekidea/boot/framework/jackson/serializer/JacksonStringSerializer.java
new file mode 100644
index 00000000..de4a6036
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/jackson/serializer/JacksonStringSerializer.java
@@ -0,0 +1,27 @@
+package io.geekidea.boot.framework.jackson.serializer;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import java.io.IOException;
+
+/**
+ * 处理json响应参数
+ *
+ * @author geekidea
+ * @date 2021-12-26
+ */
+public class JacksonStringSerializer extends JsonSerializer<String> {
+
+    public static final JacksonStringSerializer INSTANCE = new JacksonStringSerializer();
+
+    @Override
+    public void serialize(String string, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+        if (string != null) {
+            // 去除字符串空格
+            string = string.trim();
+        }
+        jsonGenerator.writeString(string);
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/framework/mybatis/YitIdGenerator.java b/src/main/java/io/geekidea/boot/framework/mybatis/YitIdGenerator.java
new file mode 100644
index 00000000..d6c0347a
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/mybatis/YitIdGenerator.java
@@ -0,0 +1,21 @@
+package io.geekidea.boot.framework.mybatis;
+
+import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
+import com.github.yitter.idgen.YitIdHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author geekidea
+ * @date 2023/11/23
+ **/
+@Slf4j
+@Component
+public class YitIdGenerator implements IdentifierGenerator {
+
+    @Override
+    public Number nextId(Object entity) {
+        return YitIdHelper.nextId();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/mybatis/config/BootMetaObjectHandler.java b/src/main/java/io/geekidea/boot/framework/mybatis/config/BootMetaObjectHandler.java
new file mode 100644
index 00000000..c9f59daa
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/mybatis/config/BootMetaObjectHandler.java
@@ -0,0 +1,44 @@
+package io.geekidea.boot.framework.mybatis.config;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import io.geekidea.boot.auth.util.CommonLoginUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+/**
+ * @author geekidea
+ * @date 2023/12/21
+ **/
+@Slf4j
+@Component
+public class BootMetaObjectHandler implements MetaObjectHandler {
+
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        log.info("start insert fill ....");
+        // 创建人
+        try {
+            this.strictInsertFill(metaObject, "createId", Long.class, CommonLoginUtil.getUserId());
+        } catch (Exception e) {
+        }
+        // 创建时间
+        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
+    }
+
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        log.info("start update fill ....");
+        // 修改人
+        try {
+            this.strictUpdateFill(metaObject, "updateId", Long.class, CommonLoginUtil.getUserId());
+        } catch (Exception e) {
+        }
+        // 修改时间
+        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
+
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/mybatis/plugins/MerchantLineInnerInterceptor.java b/src/main/java/io/geekidea/boot/framework/mybatis/plugins/MerchantLineInnerInterceptor.java
new file mode 100644
index 00000000..73b173cb
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/mybatis/plugins/MerchantLineInnerInterceptor.java
@@ -0,0 +1,289 @@
+package io.geekidea.boot.framework.mybatis.plugins;
+
+import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
+import com.baomidou.mybatisplus.core.toolkit.*;
+import com.baomidou.mybatisplus.extension.plugins.inner.BaseMultiTableInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
+import com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;
+import io.geekidea.boot.framework.mybatis.plugins.handler.MerchantLineHandler;
+import lombok.*;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.Parenthesis;
+import net.sf.jsqlparser.expression.RowConstructor;
+import net.sf.jsqlparser.expression.StringValue;
+import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
+import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
+import net.sf.jsqlparser.expression.operators.relational.ItemsList;
+import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
+import net.sf.jsqlparser.schema.Column;
+import net.sf.jsqlparser.schema.Table;
+import net.sf.jsqlparser.statement.delete.Delete;
+import net.sf.jsqlparser.statement.insert.Insert;
+import net.sf.jsqlparser.statement.select.*;
+import net.sf.jsqlparser.statement.update.Update;
+import net.sf.jsqlparser.statement.update.UpdateSet;
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlCommandType;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * 多商户拦截器
+ * 参考 {@link com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor}
+ *
+ * @author geekidea
+ * @date 2023/12/5
+ **/
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString(callSuper = true)
+@EqualsAndHashCode(callSuper = true)
+@SuppressWarnings({"rawtypes"})
+public class MerchantLineInnerInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor {
+
+    public static final String MERCHANT_LINE_HANDLER = "merchantLineHandler";
+    public static final String IGNORE = MERCHANT_LINE_HANDLER + "@true";
+
+    private static final String _COUNT = "_COUNT";
+
+    private MerchantLineHandler merchantLineHandler;
+
+    /**
+     * 处理pageHelper的_count查询过滤
+     *
+     * @param ms
+     * @return
+     */
+    private String getMappedStatementId(MappedStatement ms) {
+        String msId = ms.getId();
+        if (msId.endsWith(_COUNT)) {
+            msId = msId.substring(0, msId.lastIndexOf(_COUNT));
+        }
+        return msId;
+    }
+
+
+    @Override
+    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
+        if (merchantLineHandler.ignoreMerchantId()) {
+            return;
+        }
+        String mappedStatementId = getMappedStatementId(ms);
+        if (InterceptorIgnoreHelper.willIgnoreOthersByKey(mappedStatementId, MERCHANT_LINE_HANDLER)) {
+            return;
+        }
+        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
+        mpBs.sql(parserSingle(mpBs.sql(), null));
+    }
+
+    @Override
+    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
+        PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
+        MappedStatement ms = mpSh.mappedStatement();
+        SqlCommandType sct = ms.getSqlCommandType();
+        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
+            if (merchantLineHandler.ignoreMerchantId()) {
+                return;
+            }
+            String mappedStatementId = getMappedStatementId(ms);
+            if (InterceptorIgnoreHelper.willIgnoreOthersByKey(mappedStatementId, MERCHANT_LINE_HANDLER)) {
+                return;
+            }
+            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
+            mpBs.sql(parserMulti(mpBs.sql(), null));
+        }
+    }
+
+    @Override
+    protected void processSelect(Select select, int index, String sql, Object obj) {
+        final String whereSegment = (String) obj;
+        processSelectBody(select.getSelectBody(), whereSegment);
+        List<WithItem> withItemsList = select.getWithItemsList();
+        if (!CollectionUtils.isEmpty(withItemsList)) {
+            withItemsList.forEach(withItem -> processSelectBody(withItem, whereSegment));
+        }
+    }
+
+    @Override
+    protected void processInsert(Insert insert, int index, String sql, Object obj) {
+        if (merchantLineHandler.ignoreTable(insert.getTable().getName())) {
+            // 过滤退出执行
+            return;
+        }
+        List<Column> columns = insert.getColumns();
+        if (CollectionUtils.isEmpty(columns)) {
+            // 针对不给列名的insert 不处理
+            return;
+        }
+        String merchantIdColumn = merchantLineHandler.getMerchantIdColumn();
+        if (merchantLineHandler.ignoreInsert(columns, merchantIdColumn)) {
+            // 针对已给出商户列的insert 不处理
+            return;
+        }
+        columns.add(new Column(merchantIdColumn));
+
+        List<Expression> duplicateUpdateColumns = insert.getDuplicateUpdateExpressionList();
+        if (CollectionUtils.isNotEmpty(duplicateUpdateColumns)) {
+            EqualsTo equalsTo = new EqualsTo();
+            equalsTo.setLeftExpression(new StringValue(merchantIdColumn));
+            equalsTo.setRightExpression(merchantLineHandler.getMerchantId());
+            duplicateUpdateColumns.add(equalsTo);
+        }
+
+        Select select = insert.getSelect();
+        if (select != null && (select.getSelectBody() instanceof PlainSelect)) {
+            this.processInsertSelect(select.getSelectBody(), (String) obj);
+        } else if (insert.getItemsList() != null) {
+            ItemsList itemsList = insert.getItemsList();
+            Expression merchantId = merchantLineHandler.getMerchantId();
+            if (itemsList instanceof MultiExpressionList) {
+                ((MultiExpressionList) itemsList).getExpressionLists().forEach(el -> el.getExpressions().add(merchantId));
+            } else {
+                List<Expression> expressions = ((ExpressionList) itemsList).getExpressions();
+                if (CollectionUtils.isNotEmpty(expressions)) {
+                    int len = expressions.size();
+                    for (int i = 0; i < len; i++) {
+                        Expression expression = expressions.get(i);
+                        if (expression instanceof RowConstructor) {
+                            ((RowConstructor) expression).getExprList().getExpressions().add(merchantId);
+                        } else if (expression instanceof Parenthesis) {
+                            RowConstructor rowConstructor = new RowConstructor()
+                                    .withExprList(new ExpressionList(((Parenthesis) expression).getExpression(), merchantId));
+                            expressions.set(i, rowConstructor);
+                        } else {
+                            if (len - 1 == i) {
+                                expressions.add(merchantId);
+                            }
+                        }
+                    }
+                } else {
+                    expressions.add(merchantId);
+                }
+            }
+        } else {
+            throw ExceptionUtils.mpe("Failed to process multiple-table update, please exclude the tableName or statementId");
+        }
+    }
+
+    /**
+     * update 语句处理
+     */
+    @Override
+    protected void processUpdate(Update update, int index, String sql, Object obj) {
+        final Table table = update.getTable();
+        if (merchantLineHandler.ignoreTable(table.getName())) {
+            // 过滤退出执行
+            return;
+        }
+        ArrayList<UpdateSet> sets = update.getUpdateSets();
+        if (!CollectionUtils.isEmpty(sets)) {
+            sets.forEach(us -> us.getExpressions().forEach(ex -> {
+                if (ex instanceof SubSelect) {
+                    processSelectBody(((SubSelect) ex).getSelectBody(), (String) obj);
+                }
+            }));
+        }
+        update.setWhere(this.andExpression(table, update.getWhere(), (String) obj));
+    }
+
+    /**
+     * delete 语句处理
+     */
+    @Override
+    protected void processDelete(Delete delete, int index, String sql, Object obj) {
+        if (merchantLineHandler.ignoreTable(delete.getTable().getName())) {
+            // 过滤退出执行
+            return;
+        }
+        delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere(), (String) obj));
+    }
+
+    /**
+     * 处理 insert into select
+     * 进入这里表示需要 insert 的表启用了多商户,则 select 的表都启动了
+     *
+     * @param selectBody SelectBody
+     */
+    protected void processInsertSelect(SelectBody selectBody, final String whereSegment) {
+        PlainSelect plainSelect = (PlainSelect) selectBody;
+        FromItem fromItem = plainSelect.getFromItem();
+        if (fromItem instanceof Table) {
+            processPlainSelect(plainSelect, whereSegment);
+            appendSelectItem(plainSelect.getSelectItems());
+        } else if (fromItem instanceof SubSelect) {
+            SubSelect subSelect = (SubSelect) fromItem;
+            appendSelectItem(plainSelect.getSelectItems());
+            processInsertSelect(subSelect.getSelectBody(), whereSegment);
+        }
+    }
+
+    /**
+     * 追加 SelectItem
+     *
+     * @param selectItems SelectItem
+     */
+    protected void appendSelectItem(List<SelectItem> selectItems) {
+        if (CollectionUtils.isEmpty(selectItems)) {
+            return;
+        }
+        if (selectItems.size() == 1) {
+            SelectItem item = selectItems.get(0);
+            if (item instanceof AllColumns || item instanceof AllTableColumns) {
+                return;
+            }
+        }
+        selectItems.add(new SelectExpressionItem(new Column(merchantLineHandler.getMerchantIdColumn())));
+    }
+
+    /**
+     * 商户字段别名设置
+     * <p>merchantId 或 tableAlias.merchantId</p>
+     *
+     * @param table 表对象
+     * @return 字段
+     */
+    protected Column getAliasColumn(Table table) {
+        StringBuilder column = new StringBuilder();
+        // 该起别名就要起别名,禁止修改此处逻辑
+        if (table.getAlias() != null) {
+            column.append(table.getAlias().getName()).append(StringPool.DOT);
+        }
+        column.append(merchantLineHandler.getMerchantIdColumn());
+        return new Column(column.toString());
+    }
+
+    @Override
+    public void setProperties(Properties properties) {
+        PropertyMapper.newInstance(properties).whenNotBlank(MERCHANT_LINE_HANDLER,
+                ClassUtils::newInstance, this::setMerchantLineHandler);
+    }
+
+    /**
+     * 构建商户条件表达式
+     *
+     * @param table        表对象
+     * @param where        当前where条件
+     * @param whereSegment 所属Mapper对象全路径（在原商户拦截器功能中，这个参数并不需要参与相关判断）
+     * @return 商户条件表达式
+     * @see BaseMultiTableInnerInterceptor#buildTableExpression(Table, Expression, String)
+     */
+    @Override
+    public Expression buildTableExpression(final Table table, final Expression where, final String whereSegment) {
+        System.out.println("merchantLineHandler = " + merchantLineHandler);
+        if (merchantLineHandler.ignoreTable(table.getName())) {
+            return null;
+        }
+        return new EqualsTo(getAliasColumn(table), merchantLineHandler.getMerchantId());
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/mybatis/plugins/handler/DataScopeHandler.java b/src/main/java/io/geekidea/boot/framework/mybatis/plugins/handler/DataScopeHandler.java
new file mode 100644
index 00000000..5f57a4ef
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/mybatis/plugins/handler/DataScopeHandler.java
@@ -0,0 +1,111 @@
+package io.geekidea.boot.framework.mybatis.plugins.handler;
+
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
+import io.geekidea.boot.auth.annotation.DataScope;
+import io.geekidea.boot.auth.util.AppLoginUtil;
+import io.geekidea.boot.auth.util.LoginUtil;
+import io.geekidea.boot.common.enums.SystemType;
+import io.geekidea.boot.util.SystemTypeUtil;
+import lombok.extern.slf4j.Slf4j;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.reflect.Method;
+
+/**
+ * @author geekidea
+ * @date 2023/12/2
+ **/
+@Slf4j
+public class DataScopeHandler implements DataPermissionHandler {
+
+    private static final String _COUNT = "_COUNT";
+
+    @Override
+    public Expression getSqlSegment(Expression where, String mappedStatementId) {
+        try {
+            Class<?> clazz = Class.forName(mappedStatementId.substring(0, mappedStatementId.lastIndexOf(StringPool.DOT)));
+            String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(StringPool.DOT) + 1);
+            if (methodName.endsWith(_COUNT)) {
+                // 如果是分页获取数量方法，则截取原方法名称
+                methodName = methodName.substring(0, methodName.lastIndexOf("_"));
+            }
+            // 获取目标方法
+            Method method = getTargetMethod(clazz, methodName);
+            // 不是自定义方法，跳过
+            if (method == null) {
+                return where;
+            }
+            // 获取方法数据范围注解
+            DataScope dataScope = method.getAnnotation(DataScope.class);
+            // 如果没有指定数据范围注解，则跳过
+            if (dataScope == null) {
+                return where;
+            }
+            // 用户表别名
+            String userAlias = dataScope.userAlias();
+            // 用户列名称
+            String userIdColumn = dataScope.userIdColumn();
+            // 用户列完整名称
+            String userColumnName = userIdColumn;
+            if (StringUtils.isNotBlank(userAlias)) {
+                userColumnName = userAlias + "." + userIdColumn;
+            }
+            String dataScopeSql = null;
+            // 获取系统类型
+            SystemType systemType = SystemTypeUtil.getSystemTypeByToken();
+            if (SystemType.ADMIN == systemType) {
+                // 管理后台数据权限处理
+                // 管理员跳过
+                if (LoginUtil.isAdmin()) {
+                    return where;
+                }
+                // 获取当前登录用户ID
+                Long userId = LoginUtil.getUserId();
+                // 增加数据范围sql
+                dataScopeSql = userColumnName + " = " + userId;
+                // TODO 如果是商城，则判断商家表别名和列名称，如果有部门数据权限，则判断部门表和列别名
+                // ...
+            } else if (SystemType.APP == systemType) {
+                // APP移动端数据权限处理
+                // 获取当前登录用户ID
+                Long userId = AppLoginUtil.getUserId();
+                // 增加数据范围sql
+                dataScopeSql = userColumnName + " = " + userId;
+            }
+            if (StringUtils.isBlank(dataScopeSql)) {
+                return where;
+            }
+            Expression appendExpression = CCJSqlParserUtil.parseCondExpression(dataScopeSql);
+            if (where == null) {
+                return appendExpression;
+            }
+            return new AndExpression(where, appendExpression);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException("");
+        }
+    }
+
+    /**
+     * 获取目标方法
+     *
+     * @param clazz
+     * @param methodName
+     * @return
+     * @throws Exception
+     */
+    private Method getTargetMethod(Class<?> clazz, String methodName) throws Exception {
+        Method[] methods = clazz.getDeclaredMethods();
+        for (Method method : methods) {
+            if (method.getName().equals(methodName)) {
+                return method;
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/mybatis/plugins/handler/MerchantLineHandler.java b/src/main/java/io/geekidea/boot/framework/mybatis/plugins/handler/MerchantLineHandler.java
new file mode 100644
index 00000000..24209a05
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/mybatis/plugins/handler/MerchantLineHandler.java
@@ -0,0 +1,62 @@
+package io.geekidea.boot.framework.mybatis.plugins.handler;
+
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.schema.Column;
+
+import java.util.List;
+
+/**
+ * 商户处理器（ merchantId 行级 ）
+ *
+ * @author geekidea
+ * @date 2023/12/5
+ **/
+public interface MerchantLineHandler {
+    /**
+     * 获取商户 ID 值表达式，只支持单个 ID 值
+     *
+     * @return 商户 ID 值表达式
+     */
+    Expression getMerchantId();
+
+    /**
+     * 获取商户字段名
+     * 默认字段名叫: merchant_id
+     *
+     * @return 商户字段名
+     */
+    default String getMerchantIdColumn() {
+        return "merchant_id";
+    }
+
+    /**
+     * 根据表名判断是否忽略拼接多商户条件
+     * 默认都不进行解析拼接多商户条件
+     *
+     * @param tableName 表名
+     * @return 是否忽略, true:表示忽略，false:需要解析并拼接多商户条件
+     */
+    default boolean ignoreTable(String tableName) {
+        return true;
+    }
+
+    /**
+     * 根据登录信息判断是否忽略拼接多商户条件
+     * 默认都不进行解析拼接多商户条件
+     * @return
+     */
+   default boolean ignoreMerchantId(){
+        return true;
+   }
+
+    /**
+     * 忽略插入商户字段逻辑
+     *
+     * @param columns        插入字段
+     * @param merchantColumn 商户 ID 字段
+     * @return
+     */
+    default boolean ignoreInsert(List<Column> columns, String merchantColumn) {
+        return columns.stream().map(Column::getColumnName).anyMatch(i -> i.equalsIgnoreCase(merchantColumn));
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/framework/page/BaseOrderQuery.java b/src/main/java/io/geekidea/boot/framework/page/BaseOrderQuery.java
new file mode 100644
index 00000000..5c3a3284
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/page/BaseOrderQuery.java
@@ -0,0 +1,26 @@
+package io.geekidea.boot.framework.page;
+
+import io.geekidea.boot.framework.query.DataRangeQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 排序查询参数
+ *
+ * @author geekidea
+ * @since 2023-11-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Schema(description = "排序查询参数")
+public abstract class BaseOrderQuery extends DataRangeQuery {
+    private static final long serialVersionUID = 1569531322874458585L;
+
+    @Schema(description = "排序列名称")
+    private String orderByColumn;
+
+    @Schema(description = "排序方式 true：升序，false：降序")
+    private Boolean orderByAsc;
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/page/BasePageQuery.java b/src/main/java/io/geekidea/boot/framework/page/BasePageQuery.java
new file mode 100644
index 00000000..289b593b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/page/BasePageQuery.java
@@ -0,0 +1,41 @@
+package io.geekidea.boot.framework.page;
+
+import io.geekidea.boot.common.constant.CommonConstant;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 分页查询参数
+ *
+ * @author geekidea
+ * @since 2018-11-08
+ */
+@Data
+@Schema(description = "分页查询参数")
+public abstract class BasePageQuery extends BaseOrderQuery {
+    private static final long serialVersionUID = -6831815750258997473L;
+
+    @Schema(description = "页码,默认为1", example = "1")
+    private Integer pageIndex = CommonConstant.DEFAULT_PAGE_INDEX;
+
+    @Schema(description = "页大小,默认为10", example = "10")
+    private Integer pageSize = CommonConstant.DEFAULT_PAGE_SIZE;
+
+    @Schema(description = "关键字搜索")
+    private String keyword;
+
+    public Integer getPageIndex() {
+        if (pageIndex == null || pageIndex <= 0) {
+            pageIndex = CommonConstant.DEFAULT_PAGE_INDEX;
+        }
+        return pageIndex;
+    }
+
+    public Integer getPageSize() {
+        if (pageSize == null || pageSize <= 0) {
+            pageSize = CommonConstant.DEFAULT_PAGE_SIZE;
+        }
+        return pageSize;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/page/OrderByItem.java b/src/main/java/io/geekidea/boot/framework/page/OrderByItem.java
new file mode 100644
index 00000000..8f34338b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/page/OrderByItem.java
@@ -0,0 +1,55 @@
+package io.geekidea.boot.framework.page;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @author geekidea
+ * @date 2022/4/12
+ **/
+@Data
+@Schema(description = "分页排序")
+public class OrderByItem {
+
+    private static final String DESC = "desc";
+
+    @Schema(description = "排序列名称")
+    private String column;
+
+    @Schema(description = "排序方式 true：升序，false：降序")
+    private boolean asc = true;
+
+    public static String orderBy(String column, Boolean asc) {
+        if (asc == null) {
+            asc = true;
+        }
+        if (asc) {
+            return asc(column);
+        } else {
+            return desc(column);
+        }
+    }
+
+    public static String asc(String column) {
+        if (StringUtils.isBlank(column)) {
+            return null;
+        }
+        return column;
+    }
+
+    public static String desc(String column) {
+        if (StringUtils.isBlank(column)) {
+            return null;
+        }
+        return column + " " + DESC;
+    }
+
+    public static String orderBy(String orderBy) {
+        if (StringUtils.isBlank(orderBy)) {
+            return null;
+        }
+        return orderBy;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/page/OrderMapping.java b/src/main/java/io/geekidea/boot/framework/page/OrderMapping.java
new file mode 100644
index 00000000..040c17a4
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/page/OrderMapping.java
@@ -0,0 +1,12 @@
+package io.geekidea.boot.framework.page;
+
+import java.util.LinkedHashMap;
+
+/**
+ * @author geekidea
+ * @date 2024/1/24
+ **/
+public class OrderMapping extends LinkedHashMap<String, String> {
+
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/page/Paging.java b/src/main/java/io/geekidea/boot/framework/page/Paging.java
new file mode 100644
index 00000000..3c4e2dc2
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/page/Paging.java
@@ -0,0 +1,50 @@
+package io.geekidea.boot.framework.page;
+
+import com.github.pagehelper.Page;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 分页结果
+ *
+ * @author geekidea
+ * @date 2018-11-08
+ */
+
+@Slf4j
+@Data
+@Schema(description = "分页结果")
+public class Paging<T> implements Serializable {
+    private static final long serialVersionUID = 7302531776693980009L;
+
+    @Schema(description = "页码")
+    private Integer pageIndex;
+
+    @Schema(description = "页大小")
+    private Integer pageSize;
+
+    @Schema(description = "总行数")
+    private long total = 0;
+
+    @Schema(description = "数据列表")
+    private List<T> list = Collections.emptyList();
+
+    public Paging() {
+
+    }
+
+    public Paging(List<T> list) {
+        this.list = list;
+        Page page = (Page) list;
+        this.total = page.getTotal();
+        this.pageIndex = page.getPageNum();
+        this.pageSize = page.getPageSize();
+
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/query/DataRangeQuery.java b/src/main/java/io/geekidea/boot/framework/query/DataRangeQuery.java
new file mode 100644
index 00000000..43f284fb
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/query/DataRangeQuery.java
@@ -0,0 +1,53 @@
+package io.geekidea.boot.framework.query;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.Null;
+import java.io.Serializable;
+
+/**
+ * 数据范围查询
+ *
+ * @author geekidea
+ * @date 2022/3/11
+ **/
+@Data
+@Schema(description = "数据范围查询")
+public class DataRangeQuery implements Serializable {
+
+    private static final long serialVersionUID = 43552489665526540L;
+
+    @Schema(description = "管理后台或APP登录用户ID", hidden = true)
+    @Null(message = "非法参数")
+    private Long loginCommonUserId;
+
+    @Schema(description = "是否是管理后台的管理员", hidden = true)
+    @Null(message = "非法参数")
+    private Boolean loginAdmin;
+
+    @Schema(description = "管理后台登录用户ID", hidden = true)
+    @Null(message = "非法参数")
+    private Long loginUserId;
+
+    @Schema(description = "管理后台登录角色ID", hidden = true)
+    @Null(message = "非法参数")
+    private Long loginRoleId;
+
+    @Schema(description = "管理后台登录角色编码", hidden = true)
+    @Null(message = "非法参数")
+    private String loginRoleCode;
+
+    @Schema(description = "移动端登录用户角色ID", hidden = true)
+    @Null(message = "非法参数")
+    private Long loginAppUserRoleId;
+
+    @Schema(description = "移动端登录用户角色编码", hidden = true)
+    @Null(message = "非法参数")
+    private String loginAppUserRoleCode;
+
+    @Schema(description = "APP登录用户ID", hidden = true)
+    @Null(message = "非法参数")
+    private Long loginAppUserId;
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/redis/CustomStringRedisSerializer.java b/src/main/java/io/geekidea/boot/framework/redis/CustomStringRedisSerializer.java
new file mode 100644
index 00000000..ce2604c8
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/redis/CustomStringRedisSerializer.java
@@ -0,0 +1,39 @@
+package io.geekidea.boot.framework.redis;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+/**
+ * 自定义RedisKey前缀
+ *
+ * @author geekidea
+ * @date 2023/11/17
+ **/
+@Slf4j
+public class CustomStringRedisSerializer extends StringRedisSerializer {
+
+    private String redisKeyPrefix;
+
+    public CustomStringRedisSerializer(String redisKeyPrefix) {
+        this.redisKeyPrefix = redisKeyPrefix;
+        log.info("redisKeyPrefix:" + redisKeyPrefix);
+    }
+
+    @Override
+    public byte[] serialize(String string) {
+        if (StringUtils.isNotBlank(redisKeyPrefix) && string != null && !string.startsWith(redisKeyPrefix)) {
+            string = redisKeyPrefix + "." + string;
+        }
+        return super.serialize(string);
+    }
+
+    @Override
+    public String deserialize(byte[] bytes) {
+        String string = super.deserialize(bytes);
+        if (StringUtils.isNotBlank(redisKeyPrefix) && string != null && !string.startsWith(redisKeyPrefix)) {
+            string = redisKeyPrefix + "." + string;
+        }
+        return string;
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/framework/response/ApiCode.java b/src/main/java/io/geekidea/boot/framework/response/ApiCode.java
new file mode 100644
index 00000000..697c4003
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/response/ApiCode.java
@@ -0,0 +1,50 @@
+package io.geekidea.boot.framework.response;
+
+/**
+ * REST API 响应码
+ *
+ * @author geekidea
+ * @since 2022-3-16
+ */
+public enum ApiCode {
+
+    /**
+     * 操作成功
+     **/
+    SUCCESS(200, "操作成功"),
+    /**
+     * 操作失败
+     **/
+    FAIL(500, "操作失败"),
+    /**
+     * token已过期
+     */
+    TOKEN_EXCEPTION(5001, "Token为空或已过期，请重新登录");
+
+    private final int code;
+    private final String msg;
+
+    ApiCode(final int code, final String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+
+    public static ApiCode getApiCode(int code) {
+        ApiCode[] apiCodes = ApiCode.values();
+        for (ApiCode apiCode : apiCodes) {
+            if (apiCode.getCode() == code) {
+                return apiCode;
+            }
+        }
+        return FAIL;
+    }
+
+    public int getCode() {
+        return code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/response/ApiResult.java b/src/main/java/io/geekidea/boot/framework/response/ApiResult.java
new file mode 100644
index 00000000..53760ef3
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/response/ApiResult.java
@@ -0,0 +1,120 @@
+package io.geekidea.boot.framework.response;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.geekidea.boot.common.constant.CommonConstant;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.MDC;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * REST API 返回结果
+ *
+ * @author geekidea
+ * @since 2022-3-16
+ */
+@Data
+@Accessors(chain = true)
+@Builder
+@AllArgsConstructor
+@Schema(description = "响应结果")
+public class ApiResult<T> implements Serializable {
+
+    private static final long serialVersionUID = 7594052194764993562L;
+
+    @Schema(description = "响应编码 200：成功，500：失败")
+    private int code;
+
+    @Schema(description = "响应结果 true：成功，false：失败")
+    private boolean success;
+
+    @Schema(description = "响应消息")
+    private String msg;
+
+    @Schema(description = "响应结果数据")
+    private T data;
+
+    @Schema(description = "响应时间")
+    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date time;
+
+    @Schema(description = "日志链路ID")
+    private String traceId;
+
+    public static ApiResult success() {
+        return success(null);
+    }
+
+    public static ApiResult success(Object data) {
+        return result(ApiCode.SUCCESS, data);
+    }
+
+    public static ApiResult fail() {
+        return fail(ApiCode.FAIL);
+    }
+
+    public static ApiResult fail(String message) {
+        return fail(ApiCode.FAIL, message);
+    }
+
+    public static ApiResult fail(ApiCode apiCode) {
+        return fail(apiCode, null);
+    }
+
+    public static ApiResult fail(ApiCode apiCode, String message) {
+        if (ApiCode.SUCCESS == apiCode) {
+            throw new RuntimeException("失败结果状态码不能为" + ApiCode.SUCCESS.getCode());
+        }
+        return result(apiCode, message, null);
+    }
+
+    public static ApiResult result(boolean flag) {
+        if (flag) {
+            return success();
+        }
+        return fail();
+    }
+
+    public static ApiResult result(ApiCode apiCode) {
+        return result(apiCode, null);
+    }
+
+    public static ApiResult result(ApiCode apiCode, Object data) {
+        return result(apiCode, null, data);
+    }
+
+    public static ApiResult result(ApiCode apiCode, String message, Object data) {
+        if (apiCode == null) {
+            throw new RuntimeException("结果状态码不能为空");
+        }
+        boolean success = false;
+        int code = apiCode.getCode();
+        if (ApiCode.SUCCESS.getCode() == code) {
+            success = true;
+        }
+        String outMessage;
+        if (StringUtils.isBlank(message)) {
+            outMessage = apiCode.getMsg();
+        } else {
+            outMessage = message;
+        }
+        String traceId = MDC.get(CommonConstant.TRACE_ID);
+        return ApiResult.builder()
+                .code(code)
+                .msg(outMessage)
+                .data(data)
+                .success(success)
+                .time(new Date())
+                .traceId(traceId)
+                .build();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/runner/YitIdRunner.java b/src/main/java/io/geekidea/boot/framework/runner/YitIdRunner.java
new file mode 100644
index 00000000..44aaccb4
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/runner/YitIdRunner.java
@@ -0,0 +1,29 @@
+package io.geekidea.boot.framework.runner;
+
+import com.github.yitter.contract.IdGeneratorOptions;
+import com.github.yitter.idgen.YitIdHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author geekidea
+ * @date 2023/11/23
+ **/
+@Slf4j
+@Component
+public class YitIdRunner implements ApplicationRunner {
+
+    @Value("${workerId:0}")
+    private short workerId;
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        IdGeneratorOptions options = new IdGeneratorOptions(workerId);
+        YitIdHelper.setIdGenerator(options);
+        log.info("YitId配置初始化完成，workerId：" + workerId);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/framework/service/BaseService.java b/src/main/java/io/geekidea/boot/framework/service/BaseService.java
new file mode 100644
index 00000000..4fb918c3
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/service/BaseService.java
@@ -0,0 +1,10 @@
+package io.geekidea.boot.framework.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * @author geekidea
+ * @date 2022/3/16
+ **/
+public interface BaseService<T> extends IService<T> {
+}
diff --git a/src/main/java/io/geekidea/boot/framework/service/impl/BaseServiceImpl.java b/src/main/java/io/geekidea/boot/framework/service/impl/BaseServiceImpl.java
new file mode 100644
index 00000000..c5d047df
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/service/impl/BaseServiceImpl.java
@@ -0,0 +1,13 @@
+package io.geekidea.boot.framework.service.impl;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.framework.service.BaseService;
+
+/**
+ * @author geekidea
+ * @date 2022/3/16
+ **/
+public class BaseServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements BaseService<T> {
+
+}
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/xss/XssFilter.java b/src/main/java/io/geekidea/boot/framework/xss/XssFilter.java
similarity index 56%
rename from framework/src/main/java/io/geekidea/springbootplus/framework/core/xss/XssFilter.java
rename to src/main/java/io/geekidea/boot/framework/xss/XssFilter.java
index 18ff7c82..88ffa596 100644
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/xss/XssFilter.java
+++ b/src/main/java/io/geekidea/boot/framework/xss/XssFilter.java
@@ -1,20 +1,4 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.xss;
+package io.geekidea.boot.framework.xss;
 
 import lombok.extern.slf4j.Slf4j;
 
@@ -27,7 +11,6 @@
  *
  * @author geekidea
  * @date 2019-10-10
- * @since 1.3.1.RELEASE
  **/
 @Slf4j
 public class XssFilter implements Filter {
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/xss/XssHttpServletRequestWrapper.java b/src/main/java/io/geekidea/boot/framework/xss/XssHttpServletRequestWrapper.java
similarity index 63%
rename from framework/src/main/java/io/geekidea/springbootplus/framework/core/xss/XssHttpServletRequestWrapper.java
rename to src/main/java/io/geekidea/boot/framework/xss/XssHttpServletRequestWrapper.java
index 83a9def1..054ca5b3 100644
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/xss/XssHttpServletRequestWrapper.java
+++ b/src/main/java/io/geekidea/boot/framework/xss/XssHttpServletRequestWrapper.java
@@ -1,20 +1,4 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.xss;
+package io.geekidea.boot.framework.xss;
 
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.text.StringEscapeUtils;
@@ -27,7 +11,6 @@
  *
  * @author geekidea
  * @date 2019-10-10
- * @since 1.3.1.RELEASE
  **/
 public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
 
diff --git a/framework/src/main/java/io/geekidea/springbootplus/framework/core/xss/XssJacksonDeserializer.java b/src/main/java/io/geekidea/boot/framework/xss/XssJacksonDeserializer.java
similarity index 50%
rename from framework/src/main/java/io/geekidea/springbootplus/framework/core/xss/XssJacksonDeserializer.java
rename to src/main/java/io/geekidea/boot/framework/xss/XssJacksonDeserializer.java
index ecc36e6b..bc17cc39 100644
--- a/framework/src/main/java/io/geekidea/springbootplus/framework/core/xss/XssJacksonDeserializer.java
+++ b/src/main/java/io/geekidea/boot/framework/xss/XssJacksonDeserializer.java
@@ -1,20 +1,4 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.framework.core.xss;
+package io.geekidea.boot.framework.xss;
 
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -29,7 +13,6 @@
  *
  * @author geekidea
  * @date 2019-10-10
- * @since 1.3.1.RELEASE
  **/
 public class XssJacksonDeserializer extends JsonDeserializer<String> {
 
diff --git a/src/main/java/io/geekidea/boot/framework/xss/XssJacksonSerializer.java b/src/main/java/io/geekidea/boot/framework/xss/XssJacksonSerializer.java
new file mode 100644
index 00000000..e6757581
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/framework/xss/XssJacksonSerializer.java
@@ -0,0 +1,25 @@
+package io.geekidea.boot.framework.xss;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.text.StringEscapeUtils;
+
+import java.io.IOException;
+
+/**
+ * Jackson响应参数字符串转义处理
+ *
+ * @author geekidea
+ * @date 2019-10-10
+ **/
+@Slf4j
+public class XssJacksonSerializer extends JsonSerializer<String> {
+
+    @Override
+    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+        jsonGenerator.writeString(StringEscapeUtils.escapeHtml4(s));
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/bean/GeneratorColumnMapping.java b/src/main/java/io/geekidea/boot/generator/bean/GeneratorColumnMapping.java
new file mode 100644
index 00000000..f96cd9b9
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/bean/GeneratorColumnMapping.java
@@ -0,0 +1,34 @@
+package io.geekidea.boot.generator.bean;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 生成列类型映射
+ *
+ * @author geekidea
+ * @date 2023/12/31
+ **/
+@Data
+@Schema(description = "生成列类型映射" )
+public class GeneratorColumnMapping {
+
+    @Schema(description = "列类型" )
+    private String dataType;
+
+    @Schema(description = "属性类型" )
+    private String propertyType;
+
+    @Schema(description = "属性包名" )
+    private String packageName;
+
+    public GeneratorColumnMapping() {
+    }
+
+    public GeneratorColumnMapping(String dataType, String propertyType, String packageName) {
+        this.dataType = dataType;
+        this.propertyType = propertyType;
+        this.packageName = packageName;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/constant/GeneratorConstant.java b/src/main/java/io/geekidea/boot/generator/constant/GeneratorConstant.java
new file mode 100644
index 00000000..1d73fb46
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/constant/GeneratorConstant.java
@@ -0,0 +1,241 @@
+package io.geekidea.boot.generator.constant;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2023/12/31
+ **/
+public interface GeneratorConstant {
+
+    String DB_TINYINT = "tinyint";
+    String DB_SMALLINT = "smallint";
+    String DB_MEDIUMINT = "mediumint";
+    String DB_INT = "int";
+    String DB_INTEGER = "integer";
+    String DB_BIGINT = "bigint";
+    String DB_FLOAT = "float";
+    String DB_DOUBLE = "double";
+    String DB_DECIMAL = "decimal";
+    String DB_BIT = "bit";
+    String DB_CHAR = "char";
+    String DB_VARCHAR = "varchar";
+    String DB_TINYTEXT = "tinytext";
+    String DB_TEXT = "text";
+    String DB_MEDIUMTEXT = "mediumtext";
+    String DB_LONGTEXT = "longtext";
+    String DB_DATE = "date";
+    String DB_DATETIME = "datetime";
+    String DB_TIMESTAMP = "timestamp";
+    String DB_TIME = "time";
+    String DB_JSON = "json";
+    List<String> DB_INT_LIST = Arrays.asList(DB_TINYINT, DB_SMALLINT, DB_MEDIUMINT, DB_INT, DB_INTEGER);
+
+    String JAVA_INTEGER = "Integer";
+    String JAVA_LONG = "Long";
+    String JAVA_FLOAT = "Float";
+    String JAVA_DOUBLE = "Double";
+    String JAVA_BIG_DECIMAL = "BigDecimal";
+    String JAVA_BOOLEAN = "Boolean";
+    String JAVA_STRING = "String";
+    String JAVA_DATE = "Date";
+    String JAVA_TIME = "Time";
+    String JAVA_SERIALIZABLE = "Serializable";
+
+    /**
+     * 公共排除字段
+     */
+    List<String> COMMON_EXCLUDE_FIELDS = Arrays.asList("version", "deleted", "createId", "updateId", "createTime", "updateTime");
+    /**
+     * 填充自动插入的列
+     */
+    List<String> FILL_INSERT_FIELDS = Arrays.asList("createId", "createTime");
+    /**
+     * 填充自动修改的列
+     */
+    List<String> FILL_UPDATE_FIELDS = Arrays.asList("updateId", "updateTime");
+    /**
+     * 默认关键词默认查询列
+     */
+    List<String> KEYWORD_QUERY_FIELDS = Arrays.asList("name");
+    /**
+     * 默认APP端关键词默认查询列
+     */
+    List<String> APP_KEYWORD_QUERY_FIELDS = Arrays.asList("name");
+    /**
+     * 默认时间筛选查询列
+     */
+    String CREATE_TIME_FIELD = "createTime";
+    /**
+     * 移除表名称后缀
+     */
+    List<String> REMOVE_TABLE_NAME_SUFFIXES = Arrays.asList("表");
+
+    /**
+     * addmin名称
+     */
+    String ADMIN = "admin";
+
+    /**
+     * app前缀
+     */
+    String APP_PREFIX = "App";
+
+    /**
+     * app名称
+     */
+    String APP = "app";
+
+    /**
+     * 管理后台请求路径映射
+     */
+    String ADMIN_MAPPING = "/" + ADMIN;
+
+    /**
+     * APP请求路径映射
+     */
+    String APP_MAPPING = "/" + APP;
+
+    String SRC_MAIN = "/src/main/";
+    String SRC_MAIN_JAVA = "/src/main/java/";
+    String SRC_TEST_JAVA = "/src/test/java/";
+    String SRC_MAIN_RESOURCES = "/src/main/resources/";
+    String SRC_MAIN_RESOURCES_MAPPER = "/src/main/resources/mapper/";
+    String JAVA_DIR = "/java";
+    String VUE_DIR = "/vue";
+    String VUE_SRC = "/vue/src/";
+    String VUE_SRC_API = "/src/api/";
+    String VUE_SRC_VIEWS = "/src/views/";
+    String VUE_API_PATH = "/api/%s";
+    String VUE_LIST_PATH = "/index";
+    String MENU_SQL_PATH = "/menu/";
+
+
+    String DOT_JAVA = ".java";
+    String DOT_XML = ".xml";
+    String DOT_SQL = ".sql";
+    String DOT_JS = ".js";
+    String DOT_VUE = ".vue";
+    String POM_XML = "pom.xml";
+    String APPLICATION_YML = SRC_MAIN_RESOURCES + "application.yml";
+    String UTF8 = "UTF-8";
+    String DEFAULT_PROJECT_NAME = "spring-boot-plus";
+
+    String ENTITY_PACKAGE = "entity";
+    String DTO_PACKAGE = "dto";
+    String QUERY_PACKAGE = "query";
+    String VO_PACKAGE = "vo";
+    String ADMIN_CONTROLLER_PACKAGE = "controller";
+    String ADMIN_CONTROLLER_PACKAGE_FILE_PATH = ADMIN_CONTROLLER_PACKAGE.replaceAll("\\.", "/");
+    String APP_CONTROLLER_PACKAGE = "controller";
+    String APP_CONTROLLER_PACKAGE_FILE_PATH = APP_CONTROLLER_PACKAGE.replaceAll("\\.", "/");
+    String SERVICE_PACKAGE = "service";
+    String SERVICE_IMPL_PACKAGE = "service.impl";
+    String SERVICE_IMPL_PACKAGE_FILE_PATH = SERVICE_IMPL_PACKAGE.replaceAll("\\.", "/");
+    String MAPPER_PACKAGE = "mapper";
+
+    String DTO = "Dto";
+    String LOWER_DTO = "dto";
+    String QUERY = "Query";
+    String LOWER_QUERY = "query";
+    String VO = "Vo";
+    String CONTROLLER = "Controller";
+    String SERVICE = "Service";
+    String SERVICE_IMPL = "ServiceImpl";
+    String MAPPER = "Mapper";
+
+    String ADD = "add";
+    String UPDATE = "update";
+    String DELETE = "delete";
+    String INFO = "info";
+    String PAGE = "Page";
+    String LOWER_PAGE = "page";
+    String GET = "get";
+    String ADD_CN = "添加";
+    String UPDATE_CN = "修改";
+    String DELETE_CN = "删除";
+    String GET_CN = "获取";
+    String INFO_CN = "详情";
+    String PAGE_CN = "分页列表";
+
+    String REQUEST_MAPPING = "@RequestMapping";
+    String GET_MAPPING = "@GetMapping";
+    String POST_MAPPING = "@PostMapping";
+    String PUT_MAPPING = "@PutMapping";
+    String DELETE_MAPPING = "@DeleteMapping";
+
+    String REQUEST_BODY = "@RequestBody";
+    String PARAMETER_OBJECT = "@ParameterObject";
+
+    String REQUEST_MAPPING_FORMAT = REQUEST_MAPPING + "(\"%s\")";
+    String GET_MAPPING_FORMAT = GET_MAPPING + "(\"%s\")";
+    String POST_MAPPING_FORMAT = POST_MAPPING + "(\"%s\")";
+    String PUT_MAPPING_FORMAT = PUT_MAPPING + "(\"%s\")";
+    String DELETE_MAPPING_FORMAT = DELETE_MAPPING + "(\"%s\")";
+
+    /**
+     * 是否启用系统日志注解
+     */
+    boolean ENABLE_SYS_LOG = true;
+    /**
+     * 是否启用权限编码注解
+     */
+    boolean ENABLE_PERMISSION = true;
+
+    String ID = "id";
+
+    String APP_CONTROLLER_TEMPLATE_NAME = "appController";
+    String APP_QUERY_TEMPLATE_NAME = "appQuery";
+    String APP_VO_TEMPLATE_NAME = "appVo";
+    String CONTROLLER_TEMPLATE_NAME = "controller";
+    String DTO_TEMPLATE_NAME = "dto";
+    String ENTITY_TEMPLATE_NAME = "entity";
+    String MAPPER_TEMPLATE_NAME = "mapper";
+    String MAPPER_XML_TEMPLATE_NAME = "mapperXml";
+    String QUERY_TEMPLATE_NAME = "query";
+    String SERVICE_TEMPLATE_NAME = "service";
+    String SERVICE_IMPL_TEMPLATE_NAME = "serviceImpl";
+    String VO_TEMPLATE_NAME = "vo";
+    String MENU_SQL_TEMPLATE_NAME = "menuSql";
+    String API_TS_TEMPLATE_NAME = "apiTs";
+    String VUE_INDEX_TEMPLATE_NAME = "vueIndex";
+    String VUE_FORM_TEMPLATE_NAME = "vueForm";
+
+    String TEMPLATES_PATH = "templates/";
+
+    String APP_CONTROLLER_TEMPLATE_PATH = TEMPLATES_PATH + "appController.java.vm";
+    String APP_QUERY_TEMPLATE_PATH = TEMPLATES_PATH + "appQuery.java.vm";
+    String APP_VO_TEMPLATE_PATH = TEMPLATES_PATH + "appVo.java.vm";
+    String CONTROLLER_TEMPLATE_PATH = TEMPLATES_PATH + "controller.java.vm";
+    String DTO_TEMPLATE_PATH = TEMPLATES_PATH + "dto.java.vm";
+    String ENTITY_TEMPLATE_PATH = TEMPLATES_PATH + "entity.java.vm";
+    String MAPPER_TEMPLATE_PATH = TEMPLATES_PATH + "mapper.java.vm";
+    String MAPPER_XML_TEMPLATE_PATH = TEMPLATES_PATH + "mapper.xml.vm";
+    String QUERY_TEMPLATE_PATH = TEMPLATES_PATH + "query.java.vm";
+    String SERVICE_TEMPLATE_PATH = TEMPLATES_PATH + "service.java.vm";
+    String SERVICE_IMPL_TEMPLATE_PATH = TEMPLATES_PATH + "serviceImpl.java.vm";
+    String VO_TEMPLATE_PATH = TEMPLATES_PATH + "vo.java.vm";
+    String MENU_SQL_TEMPLATE_PATH = TEMPLATES_PATH + "menu.sql.vm";
+    String API_TS_TEMPLATE_PATH = TEMPLATES_PATH + "api.ts.vm";
+    String VUE_INDEX_TEMPLATE_PATH = TEMPLATES_PATH + "vue.index.vm";
+    String VUE_FORM_TEMPLATE_PATH = TEMPLATES_PATH + "vue.form.vm";
+
+    String MENU_SQL_FORMAT_FILE_NAME = "%s-menu.sql";
+    String API_TS_FORMAT_FILE_NAME = "%s.ts";
+    String VUE_INDEX_FILE_NAME = "index.vue";
+    String VUE_FORM_FILE_NAME = "form.vue";
+
+    /**
+     * 当前项目目录
+     */
+    String USER_DIR = System.getProperty("user.dir");
+
+    String MANAGER_NAME = "管理";
+    String LIST_NAME = "列表";
+
+    String Y = "y";
+    String YES = "yes";
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/controller/GeneratorController.java b/src/main/java/io/geekidea/boot/generator/controller/GeneratorController.java
new file mode 100644
index 00000000..372e3399
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/controller/GeneratorController.java
@@ -0,0 +1,187 @@
+package io.geekidea.boot.generator.controller;
+
+import cn.hutool.core.util.CharsetUtil;
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.common.enums.SysLogType;
+import io.geekidea.boot.framework.annotation.Log;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.generator.dto.GeneratorCodeDto;
+import io.geekidea.boot.generator.dto.GeneratorTableDto;
+import io.geekidea.boot.generator.entity.GeneratorTable;
+import io.geekidea.boot.generator.query.GeneratorTableQuery;
+import io.geekidea.boot.generator.service.GeneratorService;
+import io.geekidea.boot.generator.service.GeneratorTableService;
+import io.geekidea.boot.generator.vo.GeneratorCodeVo;
+import io.geekidea.boot.generator.vo.GeneratorTableDbVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.io.IOUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * 生成代码 控制器
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Slf4j
+@RestController
+@Tag(name = "生成代码")
+@RequestMapping("/admin/generator")
+public class GeneratorController {
+
+    @Autowired
+    private GeneratorService generatorService;
+
+    @Autowired
+    private GeneratorTableService generatorTableService;
+
+    /**
+     * 获取数据库表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getDbTablePage")
+    @Operation(summary = "获取数据库表")
+    @Permission("generator:db-table:page")
+    public ApiResult<GeneratorTableDbVo> getDbTablePage(@Valid @RequestBody GeneratorTableQuery query) {
+        Paging<GeneratorTableDbVo> paging = generatorTableService.getDbTablePage(query);
+        return ApiResult.success(paging);
+    }
+
+    /**
+     * 获取生成代码表详情
+     *
+     * @param tableName
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getGeneratorTable/{tableName}")
+    @Operation(summary = "生成代码表详情")
+    @Permission("generator:table:info")
+    public ApiResult<GeneratorTable> getGeneratorTable(@PathVariable String tableName) {
+        GeneratorTable generatorTable = generatorTableService.getGeneratorTableInfo(tableName);
+        return ApiResult.success(generatorTable);
+    }
+
+    /**
+     * 修改生成代码表
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "修改生成代码表", type = SysLogType.UPDATE)
+    @PostMapping("/updateGeneratorTable")
+    @Operation(summary = "修改生成代码表")
+    @Permission("generator:table:update")
+    public ApiResult updateGeneratorTable(@Valid @RequestBody GeneratorTableDto dto) {
+        boolean flag = generatorTableService.updateGeneratorTable(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 预览代码
+     *
+     * @param tableName
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/previewCode/{tableName}")
+    @Operation(summary = "预览代码")
+    @Permission("generator:preview-code")
+    public ApiResult<GeneratorCodeVo> previewCode(@PathVariable String tableName) {
+        List<GeneratorCodeVo> list = generatorService.previewCode(tableName);
+        return ApiResult.success(list);
+    }
+
+    /**
+     * 生成代码
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/generatorCode")
+    @Operation(summary = "生成代码")
+    @Permission("generator:generator-code")
+    public ApiResult<GeneratorCodeVo> generatorCode(@Valid @RequestBody GeneratorCodeDto dto) throws Exception {
+        log.info("生成代码开始：" + dto);
+        List<String> tableNames = dto.getTableNames();
+        generatorService.generatorCode(tableNames);
+        log.info("生成代码结束：" + dto);
+        return ApiResult.success();
+    }
+
+    /**
+     * 下载代码
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/downloadCode")
+    @Operation(summary = "下载代码")
+    @Permission("generator:download-code")
+    public void downloadCode(@Valid @RequestBody GeneratorCodeDto dto, HttpServletResponse response) throws Exception {
+        log.info("下载代码开始：" + dto);
+        List<String> tableNames = dto.getTableNames();
+        Map<String, List<GeneratorCodeVo>> map = generatorService.downloadCode(tableNames);
+        if (MapUtils.isNotEmpty(map)) {
+            // zip下载
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
+            for (Map.Entry<String, List<GeneratorCodeVo>> entry : map.entrySet()) {
+                List<GeneratorCodeVo> codeVos = entry.getValue();
+                if (CollectionUtils.isNotEmpty(codeVos)) {
+                    for (GeneratorCodeVo codeVo : codeVos) {
+                        String fileName = codeVo.getFileName();
+                        String zipFilePath = codeVo.getZipFilePath();
+                        String fileContent = codeVo.getFileContent();
+                        log.info("generatorFileName：" + fileName);
+                        log.info("generatorZipFilePath：" + zipFilePath);
+                        // 添加到zip
+                        zipOutputStream.putNextEntry(new ZipEntry(zipFilePath));
+                        IOUtils.write(fileContent, zipOutputStream, CharsetUtil.CHARSET_UTF_8);
+                        zipOutputStream.flush();
+                        zipOutputStream.closeEntry();
+                    }
+                }
+            }
+            IOUtils.closeQuietly(zipOutputStream);
+            byte[] bytes = outputStream.toByteArray();
+            String zipFileName;
+            if (tableNames.size() > 1) {
+                zipFileName = "spring-boot-plus-code.zip";
+            } else {
+                zipFileName = tableNames.get(0) + "-code.zip";
+            }
+            log.info("downloadZipFileName = " + zipFileName);
+            response.reset();
+            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + zipFileName);
+            response.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(bytes.length));
+            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
+            IOUtils.write(bytes, response.getOutputStream());
+            log.info("downloadZipFile完成");
+        }
+        log.info("下载代码结束：" + dto);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/dto/GeneratorCodeDto.java b/src/main/java/io/geekidea/boot/generator/dto/GeneratorCodeDto.java
new file mode 100644
index 00000000..8d0f4833
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/dto/GeneratorCodeDto.java
@@ -0,0 +1,25 @@
+package io.geekidea.boot.generator.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * 生成代码参数
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Data
+@Schema(description = "生成代码参数")
+public class GeneratorCodeDto {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "表名称集合")
+    @NotNull(message = "表名称不能为空")
+    private List<String> tableNames;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/generator/dto/GeneratorColumnDto.java b/src/main/java/io/geekidea/boot/generator/dto/GeneratorColumnDto.java
new file mode 100644
index 00000000..bfb37bb6
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/dto/GeneratorColumnDto.java
@@ -0,0 +1,56 @@
+package io.geekidea.boot.generator.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 修改生成代码列参数
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Data
+@Schema(description = "生成代码列Dto参数")
+public class GeneratorColumnDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "自定义注释")
+    @NotBlank(message = "注释不能为空")
+    private String columnCustomComment;
+
+    @Schema(description = "列顺序")
+    private Integer columnSort;
+
+    @Schema(description = "是否必填")
+    private Boolean isRequired;
+
+    @Schema(description = "是否表单字段")
+    private Boolean isForm;
+
+    @Schema(description = "是否列表字段")
+    private Boolean isList;
+
+    @Schema(description = "是否条件查询")
+    private Boolean isQuery;
+
+    @Schema(description = "查询方式 1：等于，2：不等于，3：大于，4：大于等于，5：小于，6：小于等于，7：like，8：日期范围，9：日期时间范围")
+    private Integer queryType;
+
+    @Schema(description = "表单类型 1：单行文本，2：多行文本，3：数字框，4：单选框，5：复选框，6：下拉框，7：日期，8：日期时间，9：时间，10：文件上传，11：富文本")
+    @NotNull(message = "表单类型不能为空")
+    private Integer formType;
+
+    @Schema(description = "字典类型编码")
+    private String dictType;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/generator/dto/GeneratorTableDto.java b/src/main/java/io/geekidea/boot/generator/dto/GeneratorTableDto.java
new file mode 100644
index 00000000..7b7f5d2b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/dto/GeneratorTableDto.java
@@ -0,0 +1,100 @@
+package io.geekidea.boot.generator.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 修改生成代码表参数
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Data
+@Schema(description = "生成代码表Dto参数")
+public class GeneratorTableDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "表注释")
+    @NotBlank(message = "表注释不能为空")
+    @Length(max = 200, message = "表注释长度超过限制")
+    private String tableComment;
+
+    @Schema(description = "类名称")
+    @NotBlank(message = "类名称不能为空")
+    @Length(max = 200, message = "类名称长度超过限制")
+    private String className;
+
+    @Schema(description = "模块名称")
+    @NotBlank(message = "模块名称不能为空")
+    @Length(max = 100, message = "模块名称长度超过限制")
+    private String moduleName;
+
+    @Schema(description = "包名称")
+    @NotBlank(message = "包名称不能为空")
+    @Length(max = 200, message = "包名称长度超过限制")
+    private String packageName;
+
+    @Schema(description = "作者")
+    @NotBlank(message = "作者不能为空")
+    @Length(max = 100, message = "作者长度超过限制")
+    private String author;
+
+    @Schema(description = "生成ID类型枚举 AUTO、NONE、INPUT、ASSIGN_ID、ASSIGN_UUID")
+    private String idType;
+
+    @Schema(description = "是否生成后端 1：是，0：否")
+    private Boolean generatorBackend;
+
+    @Schema(description = "是否生成App后端代码 1：是，0：否")
+    private Boolean generatorAppBackend;
+
+    @Schema(description = "是否生成前端 1：是，0：否")
+    private Boolean generatorFrontend;
+
+    @Schema(description = "是否校验字段 1：是，0：否")
+    private Boolean validateField;
+
+    @Schema(description = "是否启用日志注解 1：是，0：否")
+    private Boolean enableLog;
+
+    @Schema(description = "是否启用权限编码 1：是，0：否")
+    private Boolean enablePermission;
+
+    @Schema(description = "请求映射风格 1：默认，2：restful，3：全部小写，4：中横线，5：下划线")
+    private Integer requestMappingStyle;
+
+    @Schema(description = "默认降序排列名称，如id、create_time，如果为空，则不指定默认排序")
+    private String defaultOrderColumnName;
+
+    @Schema(description = "上级菜单ID")
+    private Long parentMenuId;
+
+    @Schema(description = "表单布局方式 1：一行一列，2：一行两列")
+    private Integer formLayout;
+
+    @Schema(description = "生成方式 1：zip压缩包，2：自定义路径")
+    private Integer generatorType;
+
+    @Schema(description = "自定义生成后端路径")
+    @Length(max = 300, message = "自定义生成后端路径长度超过限制")
+    private String customBackendPath;
+
+    @Schema(description = "自定义生成前端路径")
+    @Length(max = 300, message = "自定义生成前端路径长度超过限制")
+    private String customFrontendPath;
+
+    @Schema(description = "列集合")
+    private List<GeneratorColumnDto> columns;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/generator/entity/GeneratorColumn.java b/src/main/java/io/geekidea/boot/generator/entity/GeneratorColumn.java
new file mode 100644
index 00000000..52c013d1
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/entity/GeneratorColumn.java
@@ -0,0 +1,116 @@
+package io.geekidea.boot.generator.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.geekidea.boot.generator.vo.GeneratorOptionVo;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 生成代码列
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Data
+@TableName("generator_column")
+@Schema(description = "生成代码列")
+public class GeneratorColumn implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "表ID")
+    private String tableName;
+
+    @Schema(description = "字段名称")
+    private String columnName;
+
+    @Schema(description = "字段注释")
+    private String columnComment;
+
+    @Schema(description = "自定义注释")
+    private String columnCustomComment;
+
+    @Schema(description = "数据类型")
+    private String dataType;
+
+    @Schema(description = "字段类型")
+    private String columnType;
+
+    @Schema(description = "列长度")
+    private Integer columnLength;
+
+    @Schema(description = "是否存在长度")
+    private Boolean existsLength;
+
+    @Schema(description = "列顺序")
+    private Integer columnSort;
+
+    @Schema(description = "字段名称")
+    private String propertyName;
+
+    @Schema(description = "属性类型")
+    private String propertyType;
+
+    @Schema(description = "是否主键")
+    private Boolean isPk;
+
+    @Schema(description = "是否必填")
+    private Boolean isRequired;
+
+    @Schema(description = "是否有默认值")
+    private Boolean isDefaultValue;
+
+    @Schema(description = "是否校验 0：不校验，1：校验")
+    private Boolean isValidate;
+
+    @Schema(description = "是否表单字段")
+    private Boolean isForm;
+
+    @Schema(description = "是否列表字段")
+    private Boolean isList;
+
+    @Schema(description = "是否条件查询")
+    private Boolean isQuery;
+
+    @Schema(description = "查询方式 1：等于，2：不等于，3：大于，4：大于等于，5：小于，6：小于等于，7：like，8：日期范围，9：日期时间范围")
+    private Integer queryType;
+
+    @Schema(description = "表单类型 1：单行文本，2：多行文本，3：数字框，4：单选框，5：复选框，6：下拉框，7：日期，8：日期时间，9：时间，10：文件上传，11：富文本")
+    private Integer formType;
+
+    @Schema(description = "字典类型编码")
+    private String dictType;
+
+    @Schema(description = "枚举类型json")
+    private String optionJson;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+    @Schema(description = "option集合")
+    @TableField(exist = false)
+    private List<GeneratorOptionVo> options;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/generator/entity/GeneratorTable.java b/src/main/java/io/geekidea/boot/generator/entity/GeneratorTable.java
new file mode 100644
index 00000000..30ca0fb6
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/entity/GeneratorTable.java
@@ -0,0 +1,119 @@
+package io.geekidea.boot.generator.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 生成代码表
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Data
+@TableName("generator_table")
+@Schema(description = "生成代码表")
+public class GeneratorTable implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "表名称")
+    private String tableName;
+
+    @Schema(description = "表注释")
+    private String tableComment;
+
+    @Schema(description = "类名称")
+    private String className;
+
+    @Schema(description = "模块名称")
+    private String moduleName;
+
+    @Schema(description = "包名称")
+    private String packageName;
+
+    @Schema(description = "作者")
+    private String author;
+
+    @Schema(description = "生成ID类型枚举")
+    private String idType;
+
+    @Schema(description = "是否生成后端 1：是，0：否")
+    private Boolean generatorBackend;
+
+    @Schema(description = "是否生成App后端代码 1：是，0：否")
+    private Boolean generatorAppBackend;
+
+    @Schema(description = "是否生成前端 1：是，0：否")
+    private Boolean generatorFrontend;
+
+    @Schema(description = "是否校验字段 1：是，0：否")
+    private Boolean validateField;
+
+    @Schema(description = "是否启用日志注解 1：是，0：否")
+    private Boolean enableLog;
+
+    @Schema(description = "是否启用权限编码 1：是，0：否")
+    private Boolean enablePermission;
+
+    @Schema(description = "请求映射风格 1：默认，2：restful，3：全部小写，4：中横线，5：下划线")
+    private Integer requestMappingStyle;
+
+    @Schema(description = "默认降序排列名称，如id、create_time，如果为空，则不指定默认排序")
+    private String defaultOrderColumnName;
+
+    @Schema(description = "上级菜单ID")
+    private Long parentMenuId;
+
+    @Schema(description = "表单布局方式 1：一行一列，2：一行两列")
+    private Integer formLayout;
+
+    @Schema(description = "生成方式 1：zip压缩包，2：自定义路径")
+    private Integer generatorType;
+
+    @Schema(description = "自定义生成后端路径")
+    private String customBackendPath;
+
+    @Schema(description = "自定义生成前端路径")
+    private String customFrontendPath;
+
+    @Schema(description = "是否显示默认查询条件 1：是，0：否")
+    private Boolean showDefaultQuery;
+
+    @Schema(description = "是否只生成实体类 1：是，0：否")
+    private Boolean onlyGeneratorEntity;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+    @Schema(description = "主键列信息")
+    @TableField(exist = false)
+    private GeneratorColumn pkColumn;
+
+    @Schema(description = "列信息集合")
+    @TableField(exist = false)
+    private List<GeneratorColumn> columns;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/generator/enums/GeneratorFormLayout.java b/src/main/java/io/geekidea/boot/generator/enums/GeneratorFormLayout.java
new file mode 100644
index 00000000..adfb9cb7
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/enums/GeneratorFormLayout.java
@@ -0,0 +1,65 @@
+package io.geekidea.boot.generator.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 设置前端表单布局方式 1：一行一列，2：一行两列
+ *
+ * @author geekidea
+ * @date 2024/1/24
+ **/
+public enum GeneratorFormLayout {
+
+    ONE(1, "一行一列"),
+    TWO(2, "一行两列");
+
+    private Integer code;
+
+    private String desc;
+
+    GeneratorFormLayout(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, GeneratorFormLayout> map = new HashMap<>();
+
+    static {
+        for (GeneratorFormLayout type : values()) {
+            map.put(type.code, type);
+        }
+    }
+
+    public static Integer getCode(GeneratorFormLayout type) {
+        if (type == null) {
+            return TWO.code;
+        }
+        return type.code;
+    }
+
+    public static GeneratorFormLayout get(Integer code) {
+        GeneratorFormLayout type = map.get(code);
+        if (type == null) {
+            return TWO;
+        }
+        return type;
+    }
+
+    public static String getDesc(Integer code) {
+        GeneratorFormLayout type = get(code);
+        if (type == null) {
+            return null;
+        }
+        return type.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/enums/GeneratorFormType.java b/src/main/java/io/geekidea/boot/generator/enums/GeneratorFormType.java
new file mode 100644
index 00000000..e3879e1c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/enums/GeneratorFormType.java
@@ -0,0 +1,74 @@
+package io.geekidea.boot.generator.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 表单类型 1：单行文本，2：多行文本，3：数字框，4：单选框，5：复选框，6：下拉框，7：日期，8：日期时间，9：时间，10：文件上传，11：富文本
+ *
+ * @author geekidea
+ * @date 2023/12/31
+ **/
+public enum GeneratorFormType {
+
+    INPUT(1, "单行文本"),
+    TEXTAREA(2, "多行文本"),
+    NUMBER(3, "数字框"),
+    RADIO(4, "单选框"),
+    CHECKBOX(5, "复选框"),
+    SELECT(6, "下拉框"),
+    DATE(7, "日期"),
+    DATETIME(8, "日期时间"),
+    TIME(9, "日期时间"),
+    FILE(10, "文件上传"),
+    RICH_TEXT(11, "富文本");
+
+    private Integer code;
+
+    private String desc;
+
+    GeneratorFormType(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, GeneratorFormType> map = new HashMap<>();
+
+    static {
+        for (GeneratorFormType type : values()) {
+            map.put(type.code, type);
+        }
+    }
+
+    public static Integer getCode(GeneratorFormType type) {
+        if (type == null) {
+            return INPUT.code;
+        }
+        return type.code;
+    }
+
+    public static GeneratorFormType get(Integer code) {
+        GeneratorFormType type = map.get(code);
+        if (type == null) {
+            return INPUT;
+        }
+        return type;
+    }
+
+    public static String getDesc(Integer code) {
+        GeneratorFormType type = get(code);
+        if (type == null) {
+            return null;
+        }
+        return type.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/enums/GeneratorQueryType.java b/src/main/java/io/geekidea/boot/generator/enums/GeneratorQueryType.java
new file mode 100644
index 00000000..1f9fb41a
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/enums/GeneratorQueryType.java
@@ -0,0 +1,72 @@
+package io.geekidea.boot.generator.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 查询方式 1：等于，2：不等于，3：大于，4：大于等于，5：小于，6：小于等于，7：like，8：日期范围，9：日期时间范围
+ *
+ * @author geekidea
+ * @date 2023/12/31
+ **/
+public enum GeneratorQueryType {
+
+    EQ(1, "等于"),
+    NE(2, "不等于"),
+    GT(3, "大于"),
+    GE(4, "大于等于"),
+    LT(5, "小于"),
+    LE(6, "小于等于"),
+    LIKE(7, "like"),
+    DATE_RANGE(8, "日期范围"),
+    DATETIME_RANGE(9, "日期时间范围");
+
+    private Integer code;
+
+    private String desc;
+
+    GeneratorQueryType(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, GeneratorQueryType> map = new HashMap<>();
+
+    static {
+        for (GeneratorQueryType type : values()) {
+            map.put(type.code, type);
+        }
+    }
+
+    public static Integer getCode(GeneratorQueryType type) {
+        if (type == null) {
+            return EQ.code;
+        }
+        return type.code;
+    }
+
+    public static GeneratorQueryType get(Integer code) {
+        GeneratorQueryType type = map.get(code);
+        if (type == null) {
+            return EQ;
+        }
+        return type;
+    }
+
+    public static String getDesc(Integer code) {
+        GeneratorQueryType type = get(code);
+        if (type == null) {
+            return null;
+        }
+        return type.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/enums/GeneratorTemplateType.java b/src/main/java/io/geekidea/boot/generator/enums/GeneratorTemplateType.java
new file mode 100644
index 00000000..20084d6f
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/enums/GeneratorTemplateType.java
@@ -0,0 +1,66 @@
+package io.geekidea.boot.generator.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 生成代码的模板类型 1：后台代码，2：前端代码，3：菜单SQL
+ *
+ * @author geekidea
+ * @date 2024/1/22
+ **/
+public enum GeneratorTemplateType {
+
+    BACKEND(1, "后台代码"),
+    FRONTEND(2, "前端代码"),
+    MENU(3, "菜单SQL");
+
+    private Integer code;
+
+    private String desc;
+
+    GeneratorTemplateType(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, GeneratorTemplateType> map = new HashMap<>();
+
+    static {
+        for (GeneratorTemplateType type : values()) {
+            map.put(type.code, type);
+        }
+    }
+
+    public static Integer getCode(GeneratorTemplateType type) {
+        if (type == null) {
+            return null;
+        }
+        return type.code;
+    }
+
+    public static GeneratorTemplateType get(Integer code) {
+        GeneratorTemplateType type = map.get(code);
+        if (type == null) {
+            return null;
+        }
+        return type;
+    }
+
+    public static String getDesc(Integer code) {
+        GeneratorTemplateType type = get(code);
+        if (type == null) {
+            return null;
+        }
+        return type.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/enums/GeneratorType.java b/src/main/java/io/geekidea/boot/generator/enums/GeneratorType.java
new file mode 100644
index 00000000..cc85193f
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/enums/GeneratorType.java
@@ -0,0 +1,65 @@
+package io.geekidea.boot.generator.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 生成方式 1：zip压缩包，2：自定义路径
+ *
+ * @author geekidea
+ * @date 2024/1/24
+ **/
+public enum GeneratorType {
+
+    ZIP(1, "zip压缩包"),
+    CUSTOM(2, "自定义路径");
+
+    private Integer code;
+
+    private String desc;
+
+    GeneratorType(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, GeneratorType> map = new HashMap<>();
+
+    static {
+        for (GeneratorType type : values()) {
+            map.put(type.code, type);
+        }
+    }
+
+    public static Integer getCode(GeneratorType type) {
+        if (type == null) {
+            return ZIP.code;
+        }
+        return type.code;
+    }
+
+    public static GeneratorType get(Integer code) {
+        GeneratorType type = map.get(code);
+        if (type == null) {
+            return ZIP;
+        }
+        return type;
+    }
+
+    public static String getDesc(Integer code) {
+        GeneratorType type = get(code);
+        if (type == null) {
+            return null;
+        }
+        return type.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/enums/RequestMappingStyle.java b/src/main/java/io/geekidea/boot/generator/enums/RequestMappingStyle.java
new file mode 100644
index 00000000..b2830c7e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/enums/RequestMappingStyle.java
@@ -0,0 +1,68 @@
+package io.geekidea.boot.generator.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 请求映射风格 1：默认，2：restful，3：全部小写，4：中横线，5：下划线
+ *
+ * @author geekidea
+ * @date 2024/1/11
+ **/
+public enum RequestMappingStyle {
+
+    DEFAULT(1, "默认"),
+    RESTFUL(2, "restful"),
+    SMALL_LETTER(3, "全部小写字母"),
+    HYPHEN(4, "中横线连接符"),
+    UNDERLINE(5, "下划线");
+
+    private Integer code;
+
+    private String desc;
+
+    RequestMappingStyle(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, RequestMappingStyle> map = new HashMap<>();
+
+    static {
+        for (RequestMappingStyle style : values()) {
+            map.put(style.code, style);
+        }
+    }
+
+    public static Integer getCode(RequestMappingStyle type) {
+        if (type == null) {
+            return DEFAULT.code;
+        }
+        return type.code;
+    }
+
+    public static RequestMappingStyle get(Integer code) {
+        RequestMappingStyle style = map.get(code);
+        if (style == null) {
+            return DEFAULT;
+        }
+        return style;
+    }
+
+    public static String getDesc(Integer code) {
+        RequestMappingStyle style = get(code);
+        if (style == null) {
+            return null;
+        }
+        return style.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/exception/GeneratorException.java b/src/main/java/io/geekidea/boot/generator/exception/GeneratorException.java
new file mode 100644
index 00000000..e3c961f8
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/exception/GeneratorException.java
@@ -0,0 +1,15 @@
+package io.geekidea.boot.generator.exception;
+
+import io.geekidea.boot.framework.exception.BusinessException;
+
+/**
+ * @author geekidea
+ * @date 2024/1/15
+ **/
+public class GeneratorException extends BusinessException {
+
+    public GeneratorException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/mapper/GeneratorColumnMapper.java b/src/main/java/io/geekidea/boot/generator/mapper/GeneratorColumnMapper.java
new file mode 100644
index 00000000..98635c4d
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/mapper/GeneratorColumnMapper.java
@@ -0,0 +1,35 @@
+package io.geekidea.boot.generator.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.generator.entity.GeneratorColumn;
+import io.geekidea.boot.generator.vo.GeneratorColumnDbVo;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 生成代码列 Mapper 接口
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Mapper
+public interface GeneratorColumnMapper extends BaseMapper<GeneratorColumn> {
+
+    /**
+     * 获取数据库列集合
+     *
+     * @param tableName
+     * @return
+     */
+    List<GeneratorColumnDbVo> getDbColumnListByTableName(String tableName);
+
+    /**
+     * 根据表名称获取生成列集合
+     *
+     * @param tableName
+     * @return
+     */
+    List<GeneratorColumn> getGeneratorColumnList(String tableName);
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/mapper/GeneratorTableMapper.java b/src/main/java/io/geekidea/boot/generator/mapper/GeneratorTableMapper.java
new file mode 100644
index 00000000..854d7bd1
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/mapper/GeneratorTableMapper.java
@@ -0,0 +1,44 @@
+package io.geekidea.boot.generator.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.generator.entity.GeneratorTable;
+import io.geekidea.boot.generator.query.GeneratorTableQuery;
+import io.geekidea.boot.generator.vo.GeneratorTableDbVo;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 生成代码表 Mapper 接口
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Mapper
+public interface GeneratorTableMapper extends BaseMapper<GeneratorTable> {
+
+    /**
+     * 生成代码表详情
+     *
+     * @param tableName
+     * @return
+     */
+    GeneratorTable getGeneratorTableByTableName(String tableName);
+
+    /**
+     * 根据表名称获取表信息
+     *
+     * @param tableName
+     * @return
+     */
+    GeneratorTableDbVo getDbTableByTableName(String tableName);
+
+    /**
+     * 数据库中的表分页列表
+     *
+     * @param query
+     * @return
+     */
+    List<GeneratorTableDbVo> getDbTablePage(GeneratorTableQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/query/GeneratorColumnQuery.java b/src/main/java/io/geekidea/boot/generator/query/GeneratorColumnQuery.java
new file mode 100644
index 00000000..a4cf7119
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/query/GeneratorColumnQuery.java
@@ -0,0 +1,22 @@
+package io.geekidea.boot.generator.query;
+
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 生成代码列查询参数
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Data
+@Schema(description = "生成代码列查询参数")
+public class GeneratorColumnQuery extends BasePageQuery {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "表名称")
+    private String tableName;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/generator/query/GeneratorTableQuery.java b/src/main/java/io/geekidea/boot/generator/query/GeneratorTableQuery.java
new file mode 100644
index 00000000..1bc586ec
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/query/GeneratorTableQuery.java
@@ -0,0 +1,19 @@
+package io.geekidea.boot.generator.query;
+
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 生成代码表查询参数
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Data
+@Schema(description = "生成代码表查询参数")
+public class GeneratorTableQuery extends BasePageQuery {
+    private static final long serialVersionUID = 1L;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/generator/service/GeneratorColumnService.java b/src/main/java/io/geekidea/boot/generator/service/GeneratorColumnService.java
new file mode 100644
index 00000000..2d130ffa
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/service/GeneratorColumnService.java
@@ -0,0 +1,56 @@
+package io.geekidea.boot.generator.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.generator.dto.GeneratorColumnDto;
+import io.geekidea.boot.generator.entity.GeneratorColumn;
+import io.geekidea.boot.generator.vo.GeneratorColumnDbVo;
+
+import java.util.List;
+
+
+/**
+ * 生成代码列 服务接口
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+public interface GeneratorColumnService extends IService<GeneratorColumn> {
+
+    /**
+     * 添加生成代码列
+     *
+     * @param tableName
+     * @param validateField
+     * @return
+     * @throws Exception
+     */
+    boolean addGeneratorColumn(String tableName, Boolean validateField);
+
+    /**
+     * 修改生成代码列
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean updateGeneratorColumn(GeneratorColumnDto dto);
+
+    /**
+     * 根据表名称获取数据库原生列集合
+     *
+     * @param tableName
+     * @return
+     * @throws Exception
+     */
+    List<GeneratorColumnDbVo> getDbColumnListByTableName(String tableName);
+
+    /**
+     * 根据表名称获取生成列集合
+     *
+     * @param tableName
+     * @return
+     * @throws Exception
+     */
+    List<GeneratorColumn> getGeneratorColumnList(String tableName);
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/service/GeneratorService.java b/src/main/java/io/geekidea/boot/generator/service/GeneratorService.java
new file mode 100644
index 00000000..d95abe3d
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/service/GeneratorService.java
@@ -0,0 +1,43 @@
+package io.geekidea.boot.generator.service;
+
+import io.geekidea.boot.generator.vo.GeneratorCodeVo;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 生成代码 服务接口
+ *
+ * @author geekidea
+ * @since 2023-12-31
+ */
+public interface GeneratorService {
+
+    /**
+     * 预览代码
+     *
+     * @param tableName
+     * @return
+     * @throws Exception
+     */
+    List<GeneratorCodeVo> previewCode(String tableName);
+
+    /**
+     * 生成代码
+     *
+     * @param tableNames
+     * @return
+     * @throws Exception
+     */
+    void generatorCode(List<String> tableNames) throws Exception;
+
+    /**
+     * 下载代码
+     *
+     * @param tableNames
+     * @return
+     * @throws Exception
+     */
+    Map<String, List<GeneratorCodeVo>> downloadCode(List<String> tableNames);
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/service/GeneratorTableService.java b/src/main/java/io/geekidea/boot/generator/service/GeneratorTableService.java
new file mode 100644
index 00000000..6d7a956e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/service/GeneratorTableService.java
@@ -0,0 +1,55 @@
+package io.geekidea.boot.generator.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.generator.dto.GeneratorTableDto;
+import io.geekidea.boot.generator.entity.GeneratorTable;
+import io.geekidea.boot.generator.query.GeneratorTableQuery;
+import io.geekidea.boot.generator.vo.GeneratorTableDbVo;
+
+
+/**
+ * 生成代码表 服务接口
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+public interface GeneratorTableService extends IService<GeneratorTable> {
+
+    /**
+     * 添加生成代码表
+     *
+     * @param tableName
+     * @return
+     * @throws Exception
+     */
+    boolean addGeneratorTable(String tableName);
+
+    /**
+     * 修改生成代码表
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean updateGeneratorTable(GeneratorTableDto dto);
+
+    /**
+     * 生成代码表详情
+     *
+     * @param tableName
+     * @return
+     * @throws Exception
+     */
+    GeneratorTable getGeneratorTableInfo(String tableName);
+
+    /**
+     * 获取数据库表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    Paging<GeneratorTableDbVo> getDbTablePage(GeneratorTableQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/service/impl/GeneratorColumnServiceImpl.java b/src/main/java/io/geekidea/boot/generator/service/impl/GeneratorColumnServiceImpl.java
new file mode 100644
index 00000000..6ebc380d
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/service/impl/GeneratorColumnServiceImpl.java
@@ -0,0 +1,66 @@
+package io.geekidea.boot.generator.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.generator.dto.GeneratorColumnDto;
+import io.geekidea.boot.generator.entity.GeneratorColumn;
+import io.geekidea.boot.generator.mapper.GeneratorColumnMapper;
+import io.geekidea.boot.generator.service.GeneratorColumnService;
+import io.geekidea.boot.generator.util.GeneratorUtil;
+import io.geekidea.boot.generator.vo.GeneratorColumnDbVo;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 生成代码列 服务实现类
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Slf4j
+@Service
+public class GeneratorColumnServiceImpl extends ServiceImpl<GeneratorColumnMapper, GeneratorColumn> implements GeneratorColumnService {
+
+    @Autowired
+    private GeneratorColumnMapper generatorColumnMapper;
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean addGeneratorColumn(String tableName, Boolean validateField) {
+        List<GeneratorColumnDbVo> columnDbVos = getDbColumnListByTableName(tableName);
+        if (CollectionUtils.isEmpty(columnDbVos)) {
+            throw new BusinessException("没有列信息");
+        }
+        List<GeneratorColumn> columns = GeneratorUtil.getGeneratorColumns(tableName, columnDbVos, validateField);
+        return saveBatch(columns);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updateGeneratorColumn(GeneratorColumnDto dto) {
+        Long id = dto.getId();
+        GeneratorColumn generatorColumn = getById(id);
+        if (generatorColumn == null) {
+            throw new BusinessException("生成代码列不存在");
+        }
+        BeanUtils.copyProperties(dto, generatorColumn);
+        return updateById(generatorColumn);
+    }
+
+    @Override
+    public List<GeneratorColumnDbVo> getDbColumnListByTableName(String tableName) {
+        return generatorColumnMapper.getDbColumnListByTableName(tableName);
+    }
+
+    @Override
+    public List<GeneratorColumn> getGeneratorColumnList(String tableName) {
+        return generatorColumnMapper.getGeneratorColumnList(tableName);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/service/impl/GeneratorServiceImpl.java b/src/main/java/io/geekidea/boot/generator/service/impl/GeneratorServiceImpl.java
new file mode 100644
index 00000000..40836a89
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/service/impl/GeneratorServiceImpl.java
@@ -0,0 +1,52 @@
+package io.geekidea.boot.generator.service.impl;
+
+import io.geekidea.boot.generator.entity.GeneratorTable;
+import io.geekidea.boot.generator.service.GeneratorService;
+import io.geekidea.boot.generator.service.GeneratorTableService;
+import io.geekidea.boot.generator.util.GeneratorUtil;
+import io.geekidea.boot.generator.vo.GeneratorCodeVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author geekidea
+ * @date 2023/12/31
+ **/
+@Slf4j
+@Service
+public class GeneratorServiceImpl implements GeneratorService {
+
+    @Autowired
+    private GeneratorTableService generatorTableService;
+
+    @Override
+    public List<GeneratorCodeVo> previewCode(String tableName) {
+        GeneratorTable generatorTable = generatorTableService.getGeneratorTableInfo(tableName);
+        return GeneratorUtil.generatorCodeData(tableName, generatorTable);
+    }
+
+    @Override
+    public void generatorCode(List<String> tableNames) throws Exception {
+        for (String tableName : tableNames) {
+            GeneratorTable generatorTable = generatorTableService.getGeneratorTableInfo(tableName);
+            GeneratorUtil.generatorCode(tableName, generatorTable);
+        }
+    }
+
+    @Override
+    public Map<String, List<GeneratorCodeVo>> downloadCode(List<String> tableNames) {
+        Map<String, List<GeneratorCodeVo>> map = new LinkedHashMap<>();
+        for (String tableName : tableNames) {
+            GeneratorTable generatorTable = generatorTableService.getGeneratorTableInfo(tableName);
+            List<GeneratorCodeVo> generatorCodeVos = GeneratorUtil.generatorCodeData(tableName, generatorTable);
+            map.put(tableName, generatorCodeVos);
+        }
+        return map;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/service/impl/GeneratorTableServiceImpl.java b/src/main/java/io/geekidea/boot/generator/service/impl/GeneratorTableServiceImpl.java
new file mode 100644
index 00000000..3eb5cc74
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/service/impl/GeneratorTableServiceImpl.java
@@ -0,0 +1,123 @@
+package io.geekidea.boot.generator.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.config.properties.GeneratorProperties;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.generator.dto.GeneratorColumnDto;
+import io.geekidea.boot.generator.dto.GeneratorTableDto;
+import io.geekidea.boot.generator.entity.GeneratorColumn;
+import io.geekidea.boot.generator.entity.GeneratorTable;
+import io.geekidea.boot.generator.mapper.GeneratorTableMapper;
+import io.geekidea.boot.generator.query.GeneratorTableQuery;
+import io.geekidea.boot.generator.service.GeneratorColumnService;
+import io.geekidea.boot.generator.service.GeneratorTableService;
+import io.geekidea.boot.generator.util.GeneratorUtil;
+import io.geekidea.boot.generator.vo.GeneratorTableDbVo;
+import io.geekidea.boot.util.PagingUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 生成代码表 服务实现类
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Slf4j
+@Service
+public class GeneratorTableServiceImpl extends ServiceImpl<GeneratorTableMapper, GeneratorTable> implements GeneratorTableService {
+
+    @Autowired
+    private GeneratorTableMapper generatorTableMapper;
+
+    @Autowired
+    private GeneratorColumnService generatorColumnService;
+
+    @Autowired
+    private GeneratorProperties generatorProperties;
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean addGeneratorTable(String tableName) {
+        GeneratorTableDbVo generatorTableDbVo = generatorTableMapper.getDbTableByTableName(tableName);
+        if (generatorTableDbVo == null) {
+            throw new BusinessException("表不存在或不允许操作");
+        }
+        GeneratorTable generatorTable = new GeneratorTable();
+        // 设置表基础信息
+        GeneratorUtil.setGeneratorTable(generatorProperties, tableName, generatorTableDbVo, generatorTable);
+        return save(generatorTable);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updateGeneratorTable(GeneratorTableDto dto) {
+        Long id = dto.getId();
+        GeneratorTable generatorTable = getById(id);
+        if (generatorTable == null) {
+            throw new BusinessException("生成代码表不存在");
+        }
+        BeanUtils.copyProperties(dto, generatorTable);
+        boolean flag = updateById(generatorTable);
+        // 修改列
+        List<GeneratorColumnDto> columns = dto.getColumns();
+        if (CollectionUtils.isNotEmpty(columns)) {
+            for (GeneratorColumnDto column : columns) {
+                generatorColumnService.updateGeneratorColumn(column);
+            }
+        }
+        return flag;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public GeneratorTable getGeneratorTableInfo(String tableName) {
+        GeneratorTable table = generatorTableMapper.getGeneratorTableByTableName(tableName);
+        List<GeneratorColumn> columns;
+        if (table == null) {
+            // 添加生成的表
+            boolean addTableResult = addGeneratorTable(tableName);
+            if (!addTableResult) {
+                throw new BusinessException("添加生成表数据异常");
+            }
+            table = generatorTableMapper.getGeneratorTableByTableName(tableName);
+            Boolean validateField = table.getValidateField();
+            // 添加生成的列
+            generatorColumnService.addGeneratorColumn(tableName, validateField);
+        }
+        // 读取数据库表的列信息
+        columns = generatorColumnService.getGeneratorColumnList(tableName);
+        // 设置表的列信息
+        GeneratorUtil.setTableColumnInfo(table, columns);
+        return table;
+    }
+
+    @Override
+    public Paging<GeneratorTableDbVo> getDbTablePage(GeneratorTableQuery query) {
+        PagingUtil.handlePage(query);
+        List<GeneratorTableDbVo> list = generatorTableMapper.getDbTablePage(query);
+        if (CollectionUtils.isNotEmpty(list)) {
+            List<String> tablePrefixes = generatorProperties.getTablePrefixes();
+            for (GeneratorTableDbVo vo : list) {
+                String tableName = vo.getTableName();
+                String className = vo.getClassName();
+                if (StringUtils.isBlank(className)) {
+                    // 生成类名称
+                    String tableClassName = GeneratorUtil.getTableClassName(tableName, tablePrefixes);
+                    vo.setClassName(tableClassName);
+                }
+            }
+        }
+        Paging<GeneratorTableDbVo> paging = new Paging<>(list);
+        return paging;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/util/GeneratorUtil.java b/src/main/java/io/geekidea/boot/generator/util/GeneratorUtil.java
new file mode 100644
index 00000000..f9275488
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/util/GeneratorUtil.java
@@ -0,0 +1,1721 @@
+package io.geekidea.boot.generator.util;
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.fastjson2.JSON;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+import com.google.common.base.CaseFormat;
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.common.constant.SystemConstant;
+import io.geekidea.boot.common.enums.SysLogType;
+import io.geekidea.boot.config.properties.GeneratorProperties;
+import io.geekidea.boot.framework.annotation.Log;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.geekidea.boot.framework.page.OrderByItem;
+import io.geekidea.boot.framework.page.OrderMapping;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.generator.bean.GeneratorColumnMapping;
+import io.geekidea.boot.generator.constant.GeneratorConstant;
+import io.geekidea.boot.generator.entity.GeneratorColumn;
+import io.geekidea.boot.generator.entity.GeneratorTable;
+import io.geekidea.boot.generator.enums.*;
+import io.geekidea.boot.generator.exception.GeneratorException;
+import io.geekidea.boot.generator.vo.GeneratorCodeVo;
+import io.geekidea.boot.generator.vo.GeneratorColumnDbVo;
+import io.geekidea.boot.generator.vo.GeneratorOptionVo;
+import io.geekidea.boot.generator.vo.GeneratorTableDbVo;
+import io.geekidea.boot.system.entity.SysMenu;
+import io.geekidea.boot.util.IdUtil;
+import io.geekidea.boot.util.PagingUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springdoc.api.annotations.ParameterObject;
+import org.springframework.beans.BeanUtils;
+
+import java.io.File;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author geekidea
+ * @date 2023/12/30
+ **/
+@Slf4j
+public class GeneratorUtil {
+
+    /**
+     * 列类型映射集合
+     */
+    private static List<GeneratorColumnMapping> COLUMN_TYPE_MAPPINGS = new ArrayList<>();
+    /**
+     * 列类型映射Map
+     */
+    private static Map<String, GeneratorColumnMapping> COLUMN_TYPE_MAPPING_MAP;
+    /**
+     * 后台实体类代码模板Map
+     */
+    private static Map<String, String> BACKEND_ENTITY_TEMPLATE_MAP = new LinkedHashMap<>();
+    /**
+     * 后台代码模板Map
+     */
+    private static Map<String, String> BACKEND_TEMPLATE_MAP = new LinkedHashMap<>();
+    /**
+     * APP后台代码模板Map
+     */
+    private static Map<String, String> APP_BACKEND_TEMPLATE_MAP = new LinkedHashMap<>();
+    /**
+     * 前端代码模板Map
+     */
+    private static Map<String, String> FRONTEND_TEMPLATE_MAP = new LinkedHashMap<>();
+    /**
+     * 菜单SQL模板Map
+     */
+    private static Map<String, String> MENU_TEMPLATE_MAP = new LinkedHashMap<>();
+
+    static {
+        // 列类型映射
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_TINYINT, GeneratorConstant.JAVA_INTEGER, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_SMALLINT, GeneratorConstant.JAVA_INTEGER, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_MEDIUMINT, GeneratorConstant.JAVA_INTEGER, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_INT, GeneratorConstant.JAVA_INTEGER, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_INTEGER, GeneratorConstant.JAVA_INTEGER, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_BIGINT, GeneratorConstant.JAVA_LONG, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_FLOAT, GeneratorConstant.JAVA_FLOAT, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_DOUBLE, GeneratorConstant.JAVA_DOUBLE, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_DECIMAL, GeneratorConstant.JAVA_BIG_DECIMAL, "java.math.BigDecimal"));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_BIT, GeneratorConstant.JAVA_BOOLEAN, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_CHAR, GeneratorConstant.JAVA_STRING, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_VARCHAR, GeneratorConstant.JAVA_STRING, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_TINYTEXT, GeneratorConstant.JAVA_STRING, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_TEXT, GeneratorConstant.JAVA_STRING, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_MEDIUMTEXT, GeneratorConstant.JAVA_STRING, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_LONGTEXT, GeneratorConstant.JAVA_STRING, null));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_DATE, GeneratorConstant.JAVA_DATE, "java.util.Date"));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_DATETIME, GeneratorConstant.JAVA_DATE, "java.util.Date"));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_TIMESTAMP, GeneratorConstant.JAVA_DATE, "java.util.Date"));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_TIME, GeneratorConstant.JAVA_TIME, "java.sql.Time"));
+        COLUMN_TYPE_MAPPINGS.add(new GeneratorColumnMapping(GeneratorConstant.DB_JSON, GeneratorConstant.JAVA_STRING, null));
+        COLUMN_TYPE_MAPPING_MAP = COLUMN_TYPE_MAPPINGS.stream().collect(Collectors.toMap(GeneratorColumnMapping::getDataType, GeneratorColumnMapping -> GeneratorColumnMapping));
+
+        // 管理后端实体类生成模板
+        BACKEND_ENTITY_TEMPLATE_MAP.put(GeneratorConstant.ENTITY_TEMPLATE_NAME, GeneratorConstant.ENTITY_TEMPLATE_PATH);
+
+        // 管理后端生成模板
+        BACKEND_TEMPLATE_MAP.put(GeneratorConstant.ENTITY_TEMPLATE_NAME, GeneratorConstant.ENTITY_TEMPLATE_PATH);
+        BACKEND_TEMPLATE_MAP.put(GeneratorConstant.CONTROLLER_TEMPLATE_NAME, GeneratorConstant.CONTROLLER_TEMPLATE_PATH);
+        BACKEND_TEMPLATE_MAP.put(GeneratorConstant.SERVICE_TEMPLATE_NAME, GeneratorConstant.SERVICE_TEMPLATE_PATH);
+        BACKEND_TEMPLATE_MAP.put(GeneratorConstant.SERVICE_IMPL_TEMPLATE_NAME, GeneratorConstant.SERVICE_IMPL_TEMPLATE_PATH);
+        BACKEND_TEMPLATE_MAP.put(GeneratorConstant.MAPPER_TEMPLATE_NAME, GeneratorConstant.MAPPER_TEMPLATE_PATH);
+        BACKEND_TEMPLATE_MAP.put(GeneratorConstant.MAPPER_XML_TEMPLATE_NAME, GeneratorConstant.MAPPER_XML_TEMPLATE_PATH);
+        BACKEND_TEMPLATE_MAP.put(GeneratorConstant.DTO_TEMPLATE_NAME, GeneratorConstant.DTO_TEMPLATE_PATH);
+        BACKEND_TEMPLATE_MAP.put(GeneratorConstant.QUERY_TEMPLATE_NAME, GeneratorConstant.QUERY_TEMPLATE_PATH);
+        BACKEND_TEMPLATE_MAP.put(GeneratorConstant.VO_TEMPLATE_NAME, GeneratorConstant.VO_TEMPLATE_PATH);
+
+        // App后端生成模板
+        APP_BACKEND_TEMPLATE_MAP.put(GeneratorConstant.APP_CONTROLLER_TEMPLATE_NAME, GeneratorConstant.APP_CONTROLLER_TEMPLATE_PATH);
+        APP_BACKEND_TEMPLATE_MAP.put(GeneratorConstant.APP_QUERY_TEMPLATE_NAME, GeneratorConstant.APP_QUERY_TEMPLATE_PATH);
+        APP_BACKEND_TEMPLATE_MAP.put(GeneratorConstant.APP_VO_TEMPLATE_NAME, GeneratorConstant.APP_VO_TEMPLATE_PATH);
+
+        // 前端生成模板
+        FRONTEND_TEMPLATE_MAP.put(GeneratorConstant.API_TS_TEMPLATE_NAME, GeneratorConstant.API_TS_TEMPLATE_PATH);
+        FRONTEND_TEMPLATE_MAP.put(GeneratorConstant.VUE_INDEX_TEMPLATE_NAME, GeneratorConstant.VUE_INDEX_TEMPLATE_PATH);
+        FRONTEND_TEMPLATE_MAP.put(GeneratorConstant.VUE_FORM_TEMPLATE_NAME, GeneratorConstant.VUE_FORM_TEMPLATE_PATH);
+
+        // 菜单SQL生成模板
+        MENU_TEMPLATE_MAP.put(GeneratorConstant.MENU_SQL_TEMPLATE_NAME, GeneratorConstant.MENU_SQL_TEMPLATE_PATH);
+    }
+
+    /**
+     * 生成代码
+     *
+     * @param tableName
+     * @param generatorTable
+     * @throws Exception
+     */
+    public static void generatorCode(String tableName, GeneratorTable generatorTable) throws Exception {
+        List<GeneratorCodeVo> generatorCodeVos = GeneratorUtil.generatorCodeData(tableName, generatorTable);
+        if (CollectionUtils.isNotEmpty(generatorCodeVos)) {
+            for (GeneratorCodeVo generatorCodeVo : generatorCodeVos) {
+                String fileName = generatorCodeVo.getFileName();
+                String customFilePath = generatorCodeVo.getCustomFilePath();
+                log.info("generatorFileName：" + fileName);
+                log.info("generatorCustomFilePath：" + customFilePath);
+                File file = new File(customFilePath);
+                String fileContent = generatorCodeVo.getFileContent();
+                // 写数据到文件
+                FileUtils.writeStringToFile(file, fileContent, GeneratorConstant.UTF8);
+            }
+        }
+    }
+
+    /**
+     * 获取生成代码的集合数据
+     *
+     * @param tableName
+     * @param table
+     * @return
+     * @throws Exception
+     */
+    public static List<GeneratorCodeVo> generatorCodeData(String tableName, GeneratorTable table) {
+        // 根据表名称获取列集合
+        if (StringUtils.isBlank(tableName)) {
+            throw new GeneratorException("表名称不能为空");
+        }
+        if (table == null) {
+            throw new GeneratorException("表数据不存在");
+        }
+        List<GeneratorCodeVo> generatorCodeVos = new ArrayList<>();
+        // 获取模板数据
+        Map<String, Object> dataMap = getTemplateDataMap(tableName, table);
+        Boolean onlyGeneratorEntity = table.getOnlyGeneratorEntity();
+        Boolean generatorBackend = table.getGeneratorBackend();
+        Boolean generatorAppBackend = table.getGeneratorAppBackend();
+        Boolean generatorFrontend = table.getGeneratorFrontend();
+        if (onlyGeneratorEntity) {
+            // 渲染后端实体类模板数据
+            Map<String, String> backendEntityTemplateMap = GeneratorUtil.getBackendEntityTemplateMap();
+            renderTemplateData(table, generatorCodeVos, dataMap, backendEntityTemplateMap, GeneratorTemplateType.BACKEND);
+        } else {
+            if (generatorBackend) {
+                // 渲染后端模板数据
+                Map<String, String> backendTemplateMap = GeneratorUtil.getBackendTemplateMap();
+                renderTemplateData(table, generatorCodeVos, dataMap, backendTemplateMap, GeneratorTemplateType.BACKEND);
+            }
+            if (generatorBackend && generatorAppBackend) {
+                // 渲染App后端模板数据
+                Map<String, String> appBackendTemplateMap = GeneratorUtil.getAppBackendTemplateMap();
+                renderTemplateData(table, generatorCodeVos, dataMap, appBackendTemplateMap, GeneratorTemplateType.BACKEND);
+            }
+            if (generatorFrontend) {
+                // 渲染前端模板数据
+                Map<String, String> frontendTemplateMap = GeneratorUtil.getFrontendTemplateMap();
+                renderTemplateData(table, generatorCodeVos, dataMap, frontendTemplateMap, GeneratorTemplateType.FRONTEND);
+            }
+            if (generatorBackend && generatorFrontend) {
+                // 渲染菜单SQL模板数据
+                Map<String, String> menuTemplateMap = GeneratorUtil.getMenuTemplateMap();
+                renderTemplateData(table, generatorCodeVos, dataMap, menuTemplateMap, GeneratorTemplateType.MENU);
+            }
+        }
+        return generatorCodeVos;
+    }
+
+    /**
+     * 设置表信息
+     *
+     * @param config
+     * @param tableName
+     * @param generatorTableDbVo
+     * @param table
+     */
+    public static void setGeneratorTable(GeneratorProperties config, String tableName, GeneratorTableDbVo generatorTableDbVo, GeneratorTable table) {
+        // 复制配置的属性到table对象
+        BeanUtils.copyProperties(config, table);
+        // 设置表名称
+        table.setTableName(generatorTableDbVo.getTableName());
+        // 设置表注释
+        String tableComment = generatorTableDbVo.getTableComment();
+        tableComment = GeneratorUtil.getTableComment(tableComment);
+        table.setTableComment(tableComment);
+        // 表前缀
+        List<String> tablePrefixes = config.getTablePrefixes();
+        // 获取类名称
+        String className = GeneratorUtil.getTableClassName(tableName, tablePrefixes);
+        table.setClassName(className);
+        // 设置主键ID生成策略
+        table.setIdType(config.getIdType().name());
+        // 设置请求路径风格
+        table.setRequestMappingStyle(RequestMappingStyle.getCode(config.getRequestMappingStyle()));
+        // 设置父菜单ID
+        Long parentMenuId = config.getParentMenuId();
+        if (parentMenuId == null) {
+            parentMenuId = SystemConstant.ROOT_MENU_ID;
+        }
+        table.setParentMenuId(parentMenuId);
+        // 设置表单布局方式
+        table.setFormLayout(GeneratorFormLayout.getCode(config.getFormLayout()));
+        // 设置代码生成方式
+        table.setGeneratorType(GeneratorType.getCode(config.getGeneratorType()));
+    }
+
+    /**
+     * 设置表的列信息
+     *
+     * @param table
+     * @param columns
+     */
+    public static void setTableColumnInfo(GeneratorTable table, List<GeneratorColumn> columns) {
+        if (CollectionUtils.isNotEmpty(columns)) {
+            table.setColumns(columns);
+            // 设置主键列
+            GeneratorColumn pkColumn = GeneratorUtil.getPkColumn(columns);
+            table.setPkColumn(pkColumn);
+            // 设置列的options集合对象
+            GeneratorUtil.setColumnOptions(columns);
+        }
+    }
+
+    /**
+     * 获取生成列集合
+     *
+     * @param tableName
+     * @param columnDbVos
+     * @return
+     * @throws Exception
+     */
+    public static List<GeneratorColumn> getGeneratorColumns(String tableName, List<GeneratorColumnDbVo> columnDbVos, Boolean validateField) {
+        if (StringUtils.isBlank(tableName)) {
+            throw new GeneratorException("表名称不能为空");
+        }
+        if (CollectionUtils.isEmpty(columnDbVos)) {
+            throw new GeneratorException("没有列信息");
+        }
+        List<GeneratorColumn> columns = new ArrayList<>();
+        for (GeneratorColumnDbVo columnDbVo : columnDbVos) {
+            String columnName = columnDbVo.getColumnName();
+            String columnComment = columnDbVo.getColumnComment();
+            String columnCustomComment = GeneratorUtil.getColumnCustomComment(columnComment);
+            String dataType = columnDbVo.getDataType();
+            String columnType = columnDbVo.getColumnType();
+            Integer columnLength = GeneratorUtil.getColumnLength(columnType);
+            Integer columnSort = columnDbVo.getColumnSort();
+            GeneratorColumn column = new GeneratorColumn();
+            column.setTableName(columnDbVo.getTableName());
+            column.setColumnName(columnName);
+            column.setColumnComment(columnComment);
+            column.setDataType(dataType);
+            column.setColumnType(columnType);
+            column.setColumnLength(columnLength);
+            column.setColumnSort(columnSort);
+            // 是否长度校验
+            boolean existsLength = isExistsLength(columnLength);
+            column.setExistsLength(existsLength);
+            // 属性名称
+            String propertyName = GeneratorUtil.underlineToCamel(columnName);
+            column.setPropertyName(propertyName);
+            if (StringUtils.isBlank(columnCustomComment)) {
+                columnCustomComment = propertyName;
+            }
+            column.setColumnCustomComment(columnCustomComment);
+            String propertyType = getPropertyType(dataType, columnLength);
+            column.setPropertyType(propertyType);
+            Boolean isPk = columnDbVo.getIsPk();
+            Boolean isRequired = columnDbVo.getIsRequired();
+            Boolean isDefaultValue = columnDbVo.getIsDefaultValue();
+            column.setIsPk(isPk);
+            column.setIsRequired(isRequired);
+            column.setIsDefaultValue(isDefaultValue);
+            column.setIsValidate(false);
+            // 是否校验字段
+            if (validateField) {
+                // 如果不是主键，且是必填字段，没有默认值，则设置为需要校验
+                if (!isPk && isRequired && !isDefaultValue) {
+                    column.setIsValidate(true);
+                }
+            }
+            column.setQueryType(GeneratorQueryType.EQ.getCode());
+            column.setIsForm(false);
+            column.setIsList(false);
+            column.setIsQuery(false);
+            if (isPk) {
+                column.setIsRequired(false);
+            } else if (GeneratorConstant.KEYWORD_QUERY_FIELDS.contains(propertyName)) {
+                column.setIsForm(true);
+                column.setIsList(true);
+            } else if (GeneratorConstant.CREATE_TIME_FIELD.equals(propertyName)) {
+                column.setIsList(true);
+                column.setQueryType(GeneratorQueryType.DATE_RANGE.getCode());
+            } else if (GeneratorConstant.DB_DATE.equals(dataType)) {
+                column.setQueryType(GeneratorQueryType.DATE_RANGE.getCode());
+            } else if (GeneratorConstant.DB_DATETIME.equals(dataType)) {
+                column.setQueryType(GeneratorQueryType.DATETIME_RANGE.getCode());
+            } else if (!GeneratorConstant.COMMON_EXCLUDE_FIELDS.contains(propertyName)) {
+                column.setIsForm(true);
+                column.setIsList(true);
+            }
+            String optionJson = getOptionJson(columnComment, columnCustomComment, dataType);
+            column.setOptionJson(optionJson);
+            Integer formType = getFormType(dataType, columnLength, propertyType, optionJson);
+            column.setFormType(formType);
+            column.setDictType(null);
+            columns.add(column);
+        }
+        return columns;
+    }
+
+
+    /**
+     * 设置列的options集合对象
+     *
+     * @param columns
+     */
+    public static void setColumnOptions(List<GeneratorColumn> columns) {
+        for (GeneratorColumn column : columns) {
+            setColumnOptions(column);
+        }
+    }
+
+    /**
+     * 设置列的options集合对象
+     *
+     * @param column
+     */
+    public static void setColumnOptions(GeneratorColumn column) {
+        String optionJson = column.getOptionJson();
+        if (StringUtils.isBlank(optionJson)) {
+            return;
+        }
+        List<GeneratorOptionVo> optionVos = JSON.parseArray(optionJson, GeneratorOptionVo.class);
+        if (CollectionUtils.isNotEmpty(optionVos)) {
+            Integer formType = column.getFormType();
+            if (formType == 4) {
+                for (GeneratorOptionVo option : optionVos) {
+                    String value = option.getValue();
+                    if ("1".equals(value)) {
+                        option.setValue("true");
+                    } else {
+                        option.setValue("false");
+                    }
+                }
+            }
+        }
+        column.setOptions(optionVos);
+    }
+
+    /**
+     * 获取列表单类型
+     *
+     * @param dataType
+     * @param columnLength
+     * @param propertyType
+     * @param optionJson
+     * @return
+     */
+    public static Integer getFormType(String dataType, Integer columnLength, String propertyType, String optionJson) {
+        // 表单类型 1：单行文本，2：多行文本，3：数字框，4：单选框，5：复选框，6：下拉框，7：日期，8：日期时间，9：时间，10：文件上传，11：富文本
+        Integer formType = GeneratorFormType.INPUT.getCode();
+        if (GeneratorConstant.JAVA_STRING.equals(propertyType)) {
+            if (GeneratorConstant.DB_TINYTEXT.equals(dataType)
+                    || GeneratorConstant.DB_TEXT.equals(dataType)
+                    || GeneratorConstant.DB_MEDIUMTEXT.equals(dataType)
+                    || GeneratorConstant.DB_LONGTEXT.equals(dataType)) {
+                // 2：多行文本
+                formType = GeneratorFormType.TEXTAREA.getCode();
+            } else {
+                if (columnLength != null && columnLength <= 300) {
+                    // 1：单行文本
+                    formType = GeneratorFormType.INPUT.getCode();
+                } else {
+                    // 2：多行文本
+                    formType = GeneratorFormType.TEXTAREA.getCode();
+                }
+            }
+        } else if (GeneratorConstant.JAVA_BOOLEAN.equals(propertyType)) {
+            // 4：单选框
+            formType = GeneratorFormType.RADIO.getCode();
+        } else if (GeneratorConstant.JAVA_INTEGER.equals(propertyType) && GeneratorConstant.DB_INT_LIST.contains(dataType)) {
+            if (StringUtils.isBlank(optionJson)) {
+                // 6：数字框
+                formType = GeneratorFormType.NUMBER.getCode();
+            } else {
+                int length = optionJson.length();
+                if (length <= 2) {
+                    // 4：单选框
+                    formType = GeneratorFormType.RADIO.getCode();
+                } else {
+                    // 6：下拉框
+                    formType = GeneratorFormType.SELECT.getCode();
+                }
+            }
+        } else if (GeneratorConstant.JAVA_INTEGER.equals(propertyType)
+                || GeneratorConstant.JAVA_LONG.equals(propertyType)
+                || GeneratorConstant.JAVA_FLOAT.equals(propertyType)
+                || GeneratorConstant.JAVA_DOUBLE.equals(propertyType)
+                || GeneratorConstant.JAVA_BIG_DECIMAL.equals(propertyType)) {
+            // 3：数字框
+            formType = GeneratorFormType.NUMBER.getCode();
+        } else if (GeneratorConstant.JAVA_DATE.equals(propertyType)) {
+            if (GeneratorConstant.DB_DATE.equals(dataType)) {
+                // 7：日期
+                formType = GeneratorFormType.DATE.getCode();
+            } else if (GeneratorConstant.DB_DATETIME.equals(dataType) || GeneratorConstant.DB_TIMESTAMP.equals(dataType)) {
+                // 8：日期时间
+                formType = GeneratorFormType.DATETIME.getCode();
+            } else {
+                // 7：日期
+                formType = GeneratorFormType.DATE.getCode();
+            }
+        } else if (GeneratorConstant.JAVA_TIME.equals(propertyType)) {
+            // 9：时间
+            formType = GeneratorFormType.TIME.getCode();
+        }
+        return formType;
+    }
+
+    /**
+     * 是否存在长度
+     *
+     * @param columnLength
+     * @return
+     */
+    public static boolean isExistsLength(Integer columnLength) {
+        if (columnLength != null && columnLength > 0) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 获取主键列
+     *
+     * @param columns
+     * @return
+     */
+    public static GeneratorColumn getPkColumn(List<GeneratorColumn> columns) {
+        if (CollectionUtils.isNotEmpty(columns)) {
+            for (GeneratorColumn column : columns) {
+                if (column.getIsPk()) {
+                    return column;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 渲染模板数据
+     *
+     * @param table
+     * @param generatorCodeVos
+     * @param dataMap
+     * @param templateMap
+     * @param generatorTemplateType
+     * @throws Exception
+     */
+    public static void renderTemplateData(GeneratorTable table, List<GeneratorCodeVo> generatorCodeVos, Map<String, Object> dataMap, Map<String, String> templateMap, GeneratorTemplateType generatorTemplateType) {
+        // 获取后端模板
+        for (Map.Entry<String, String> entry : templateMap.entrySet()) {
+            String templateName = entry.getKey();
+            // 获取生成的文件
+            GeneratorCodeVo generatorCodeVo = new GeneratorCodeVo();
+            // 设置生成文件信息
+            setGeneratorFile(table, dataMap, templateName, generatorCodeVo);
+            // 模板路径
+            String templatePath = entry.getValue();
+            // 渲染模板数据
+            String fileContent = VelocityUtil.writer(templatePath, dataMap);
+            // 组装返回数据
+            generatorCodeVo.setFileContent(fileContent);
+            generatorCodeVo.setTemplateType(GeneratorTemplateType.getCode(generatorTemplateType));
+            generatorCodeVos.add(generatorCodeVo);
+        }
+    }
+
+    /**
+     * 获取模板数据Map
+     *
+     * @param tableName
+     * @param table
+     */
+    public static Map<String, Object> getTemplateDataMap(String tableName, GeneratorTable table) {
+        Map<String, Object> dataMap = new HashMap<>();
+        // 设置表信息
+        String tableComment = table.getTableComment();
+        String className = table.getClassName();
+        String packageName = table.getPackageName();
+        String moduleName = table.getModuleName();
+        String author = table.getAuthor();
+        String idType = table.getIdType();
+        Boolean generatorAppBackend = table.getGeneratorAppBackend();
+        Boolean validateField = table.getValidateField();
+        Boolean enableLog = table.getEnableLog();
+        Boolean enablePermission = table.getEnablePermission();
+        Integer requestMappingStyle = table.getRequestMappingStyle();
+        Long parentMenuId = table.getParentMenuId();
+        Integer formLayout = table.getFormLayout();
+        Integer generatorType = table.getGeneratorType();
+        String customBackendPath = table.getCustomBackendPath();
+        String customFrontendPath = table.getCustomFrontendPath();
+        Boolean showDefaultQuery = table.getShowDefaultQuery();
+        GeneratorColumn pkColumn = table.getPkColumn();
+        List<GeneratorColumn> columns = table.getColumns();
+        dataMap.put("table", table);
+        dataMap.put("tableName", tableName);
+        dataMap.put("tableComment", tableComment);
+        dataMap.put("packageName", packageName);
+        dataMap.put("moduleName", moduleName);
+        dataMap.put("author", author);
+        dataMap.put("idType", idType);
+        dataMap.put("generatorAppBackend", generatorAppBackend);
+        dataMap.put("validateField", validateField);
+        dataMap.put("enableLog", enableLog);
+        dataMap.put("enablePermission", enablePermission);
+        dataMap.put("defaultOrderColumnName", table.getDefaultOrderColumnName());
+        dataMap.put("requestMappingStyle", requestMappingStyle);
+        dataMap.put("parentMenuId", parentMenuId);
+        dataMap.put("formLayout", formLayout);
+        dataMap.put("generatorType", generatorType);
+        dataMap.put("customBackendPath", customBackendPath);
+        dataMap.put("customFrontendPath", customFrontendPath);
+        dataMap.put("showDefaultQuery", showDefaultQuery);
+        dataMap.put("columns", columns);
+
+        // 前端需要生成的列集合
+        if (CollectionUtils.isNotEmpty(columns)) {
+            // 前端表单列集合
+            List<GeneratorColumn> formColumns = new ArrayList<>();
+            // 默认keyword查询列集合
+            List<GeneratorColumn> keywordColumns = new ArrayList<>();
+            // 默认app端keyword查询列集合
+            List<GeneratorColumn> appKeywordColumns = new ArrayList<>();
+            // 前端查询列集合
+            List<GeneratorColumn> queryColumns = new ArrayList<>();
+            // 前端表格列集合
+            List<GeneratorColumn> tableColumns = new ArrayList<>();
+            for (GeneratorColumn column : columns) {
+                // 属性名称
+                String propertyName = column.getPropertyName();
+                // 前端表单列
+                if (!GeneratorConstant.COMMON_EXCLUDE_FIELDS.contains(propertyName) && !column.getIsPk()) {
+                    formColumns.add(column);
+                }
+                // 默认keyword查询列
+                if (GeneratorConstant.KEYWORD_QUERY_FIELDS.contains(propertyName)) {
+                    keywordColumns.add(column);
+                }
+                // 默认app端keyword查询列
+                if (GeneratorConstant.APP_KEYWORD_QUERY_FIELDS.contains(propertyName)) {
+                    appKeywordColumns.add(column);
+                }
+                // 前端查询列
+                Boolean isQuery = column.getIsQuery();
+                if (isQuery != null && isQuery) {
+                    queryColumns.add(column);
+                }
+                // 前端表格列
+                Boolean isList = column.getIsList();
+                if (isList != null && isList) {
+                    tableColumns.add(column);
+                }
+            }
+            dataMap.put("formColumns", formColumns);
+            dataMap.put("keywordColumns", keywordColumns);
+            dataMap.put("appKeywordColumns", appKeywordColumns);
+            dataMap.put("queryColumns", queryColumns);
+            dataMap.put("tableColumns", tableColumns);
+        }
+
+
+        String appTableComment = GeneratorConstant.APP_PREFIX + tableComment;
+        dataMap.put("appTableComment", appTableComment);
+
+        // 设置主键信息
+        dataMap.put("pkColumn", pkColumn);
+        String pkIdPropertyType;
+        String pkIdColumnName;
+        String pkIdPropertyName;
+        if (pkColumn != null) {
+            pkIdPropertyType = pkColumn.getPropertyType();
+            pkIdColumnName = pkColumn.getColumnName();
+            pkIdPropertyName = pkColumn.getPropertyName();
+        } else {
+            pkIdPropertyType = GeneratorConstant.JAVA_SERIALIZABLE;
+            pkIdColumnName = GeneratorConstant.ID;
+            pkIdPropertyName = GeneratorConstant.ID;
+            dataMap.put("existsSerializableType", true);
+        }
+        dataMap.put("pkIdPropertyType", pkIdPropertyType);
+        dataMap.put("pkIdColumnName", pkIdColumnName);
+        dataMap.put("pkIdPropertyName", pkIdPropertyName);
+        dataMap.put("pascalPkIdPropertyName", GeneratorUtil.camelToPascal(pkIdPropertyName));
+
+        // 设置包名称
+        String modulePackageName = packageName + "." + moduleName + ".";
+        String entityPackage = modulePackageName + GeneratorConstant.ENTITY_PACKAGE;
+        String dtoPackage = modulePackageName + GeneratorConstant.DTO_PACKAGE;
+        String voPackage = modulePackageName + GeneratorConstant.VO_PACKAGE;
+        String queryPackage = modulePackageName + GeneratorConstant.QUERY_PACKAGE;
+        String controllerPackage = modulePackageName + GeneratorConstant.ADMIN_CONTROLLER_PACKAGE;
+        String appControllerPackage = modulePackageName + GeneratorConstant.APP_CONTROLLER_PACKAGE;
+        String servicePackage = modulePackageName + GeneratorConstant.SERVICE_PACKAGE;
+        String serviceImplPackage = modulePackageName + GeneratorConstant.SERVICE_IMPL_PACKAGE;
+        String mapperPackage = modulePackageName + GeneratorConstant.MAPPER_PACKAGE;
+        dataMap.put("entityPackage", entityPackage);
+        dataMap.put("dtoPackage", dtoPackage);
+        dataMap.put("voPackage", voPackage);
+        dataMap.put("queryPackage", queryPackage);
+        dataMap.put("controllerPackage", controllerPackage);
+        dataMap.put("appControllerPackage", appControllerPackage);
+        dataMap.put("servicePackage", servicePackage);
+        dataMap.put("serviceImplPackage", serviceImplPackage);
+        dataMap.put("mapperPackage", mapperPackage);
+
+        // 设置类名称
+        String entity = className;
+        String dtoName = entity + GeneratorConstant.DTO;
+        String queryName = entity + GeneratorConstant.QUERY;
+        String appQueryName = GeneratorConstant.APP_PREFIX + entity + GeneratorConstant.QUERY;
+        String appVoName = GeneratorConstant.APP_PREFIX + entity + GeneratorConstant.VO;
+        String voName = entity + GeneratorConstant.VO;
+        String controllerName = entity + GeneratorConstant.CONTROLLER;
+        String appControllerName = GeneratorConstant.APP_PREFIX + entity + GeneratorConstant.CONTROLLER;
+        String serviceName = entity + GeneratorConstant.SERVICE;
+        String serviceImplName = entity + GeneratorConstant.SERVICE_IMPL;
+        String mapperName = entity + GeneratorConstant.MAPPER;
+        String mapperXmlName = mapperName;
+        dataMap.put("entity", entity);
+        dataMap.put("dtoName", dtoName);
+        dataMap.put("queryName", queryName);
+        dataMap.put("appQueryName", appQueryName);
+        dataMap.put("appVoName", appVoName);
+        dataMap.put("voName", voName);
+        dataMap.put("controllerName", controllerName);
+        dataMap.put("appControllerName", appControllerName);
+        dataMap.put("serviceName", serviceName);
+        dataMap.put("serviceImplName", serviceImplName);
+        dataMap.put("mapperName", mapperName);
+        dataMap.put("mapperXmlName", mapperXmlName);
+        dataMap.put("superServiceName", IService.class.getSimpleName());
+        dataMap.put("superServiceImplName", ServiceImpl.class.getSimpleName());
+        dataMap.put("superMapperName", BaseMapper.class.getSimpleName());
+        dataMap.put("superPageQueryName", BasePageQuery.class.getSimpleName());
+
+        // 设置对象名称
+        String entityObjectName = GeneratorUtil.pascalToCamel(entity);
+        String dtoObjectName = GeneratorConstant.LOWER_DTO;
+        String queryObjectName = GeneratorConstant.LOWER_QUERY;
+        String appQueryObjectName = GeneratorConstant.LOWER_QUERY;
+        String voObjectName = GeneratorUtil.pascalToCamel(voName);
+        String appVoObjectName = GeneratorUtil.pascalToCamel(appVoName);
+        String serviceObjectName = GeneratorUtil.pascalToCamel(serviceName);
+        String mapperObjectName = GeneratorUtil.pascalToCamel(mapperName);
+        dataMap.put("entityObjectName", entityObjectName);
+        dataMap.put("dtoObjectName", dtoObjectName);
+        dataMap.put("queryObjectName", queryObjectName);
+        dataMap.put("appQueryObjectName", appQueryObjectName);
+        dataMap.put("voObjectName", voObjectName);
+        dataMap.put("appVoObjectName", appVoObjectName);
+        dataMap.put("serviceObjectName", serviceObjectName);
+        dataMap.put("mapperObjectName", mapperObjectName);
+
+        // 设置类的包全称
+        String entityPackagePath = entityPackage + "." + entity;
+        String dtoPackagePath = dtoPackage + "." + dtoName;
+        String queryPackagePath = queryPackage + "." + queryName;
+        String appQueryPackagePath = queryPackage + "." + appQueryName;
+        String voPackagePath = voPackage + "." + voName;
+        String appVoPackagePath = voPackage + "." + appVoName;
+        String controllerPackagePath = controllerPackage + "." + controllerName;
+        String appControllerPackagePath = appControllerPackage + "." + appControllerName;
+        String servicePackagePath = servicePackage + "." + serviceName;
+        String serviceImplPackagePath = serviceImplPackage + "." + serviceImplName;
+        String mapperPackagePath = mapperPackage + "." + mapperName;
+        dataMap.put("entityPackagePath", entityPackagePath);
+        dataMap.put("dtoPackagePath", dtoPackagePath);
+        dataMap.put("queryPackagePath", queryPackagePath);
+        dataMap.put("appQueryPackagePath", appQueryPackagePath);
+        dataMap.put("voPackagePath", voPackagePath);
+        dataMap.put("appVoPackagePath", appVoPackagePath);
+        dataMap.put("controllerPackagePath", controllerPackagePath);
+        dataMap.put("appControllerPackagePath", appControllerPackagePath);
+        dataMap.put("servicePackagePath", servicePackagePath);
+        dataMap.put("serviceImplPackagePath", serviceImplPackagePath);
+        dataMap.put("mapperPackagePath", mapperPackagePath);
+        dataMap.put("sysLogTypePackagePath", SysLogType.class.getName());
+        dataMap.put("logPackagePath", Log.class.getName());
+        dataMap.put("permissionPackagePath", Permission.class.getName());
+        dataMap.put("pagingPackagePath", Paging.class.getName());
+        dataMap.put("apiResultPackagePath", ApiResult.class.getName());
+        dataMap.put("superServicePackagePath", IService.class.getName());
+        dataMap.put("superServiceImplPackagePath", ServiceImpl.class.getName());
+        dataMap.put("superMapperPackagePath", BaseMapper.class.getName());
+        dataMap.put("businessExceptionPackagePath", BusinessException.class.getName());
+        dataMap.put("orderByItemPackagePath", OrderByItem.class.getName());
+        dataMap.put("pagingUtilPackagePath", PagingUtil.class.getName());
+        dataMap.put("superPageQueryPackagePath", BasePageQuery.class.getName());
+        dataMap.put("parameterObjectPackagePath", ParameterObject.class.getName());
+        dataMap.put("orderMappingPackagePath", OrderMapping.class.getName());
+
+        // controller请求映射路径及请求方式
+        handleRequestMapping(requestMappingStyle, dataMap, pkIdPropertyName, entity, entityObjectName);
+
+        // CRUD方法名称
+        String addMethodName = GeneratorConstant.ADD + entity;
+        String updateMethodName = GeneratorConstant.UPDATE + entity;
+        String deleteMethodName = GeneratorConstant.DELETE + entity;
+        String infoMethodName = GeneratorConstant.GET + entity;
+        String pageMethodName = GeneratorConstant.GET + entity + GeneratorConstant.PAGE;
+        String appInfoMethodName = GeneratorConstant.GET + GeneratorConstant.APP_PREFIX + entity;
+        String appPageMethodName = GeneratorConstant.GET + GeneratorConstant.APP_PREFIX + entity + GeneratorConstant.PAGE;
+        dataMap.put("addMethodName", addMethodName);
+        dataMap.put("updateMethodName", updateMethodName);
+        dataMap.put("deleteMethodName", deleteMethodName);
+        dataMap.put("infoMethodName", infoMethodName);
+        dataMap.put("pageMethodName", pageMethodName);
+        dataMap.put("appInfoMethodName", appInfoMethodName);
+        dataMap.put("appPageMethodName", appPageMethodName);
+
+        // 权限编码
+        String addPermissionCode = null;
+        String updatePermissionCode = null;
+        String deletePermissionCode = null;
+        String infoPermissionCode = null;
+        String pagePermissionCode = null;
+        if (enablePermission) {
+            String tableColon = GeneratorUtil.underlineToColon(tableName);
+            addPermissionCode = tableColon + ":" + GeneratorConstant.ADD;
+            updatePermissionCode = tableColon + ":" + GeneratorConstant.UPDATE;
+            deletePermissionCode = tableColon + ":" + GeneratorConstant.DELETE;
+            infoPermissionCode = tableColon + ":" + GeneratorConstant.INFO;
+            pagePermissionCode = tableColon + ":" + GeneratorConstant.LOWER_PAGE;
+            dataMap.put("addPermissionCode", addPermissionCode);
+            dataMap.put("updatePermissionCode", updatePermissionCode);
+            dataMap.put("deletePermissionCode", deletePermissionCode);
+            dataMap.put("infoPermissionCode", infoPermissionCode);
+            dataMap.put("pagePermissionCode", pagePermissionCode);
+        }
+
+        // 方法中文注释
+        String addCnComment = GeneratorConstant.ADD_CN + tableComment;
+        String updateCnComment = GeneratorConstant.UPDATE_CN + tableComment;
+        String deleteCnComment = GeneratorConstant.DELETE_CN + tableComment;
+        String infoCnComment = GeneratorConstant.GET_CN + tableComment + GeneratorConstant.INFO_CN;
+        String pageCnComment = GeneratorConstant.GET_CN + tableComment + GeneratorConstant.PAGE_CN;
+        String appInfoCnComment = GeneratorConstant.GET_CN + GeneratorConstant.APP_PREFIX + tableComment + GeneratorConstant.INFO_CN;
+        String appPageCnComment = GeneratorConstant.GET_CN + GeneratorConstant.APP_PREFIX + tableComment + GeneratorConstant.PAGE_CN;
+        dataMap.put("addCnComment", addCnComment);
+        dataMap.put("updateCnComment", updateCnComment);
+        dataMap.put("deleteCnComment", deleteCnComment);
+        dataMap.put("infoCnComment", infoCnComment);
+        dataMap.put("pageCnComment", pageCnComment);
+        dataMap.put("appInfoCnComment", appInfoCnComment);
+        dataMap.put("appPageCnComment", appPageCnComment);
+
+        // 其它
+        dataMap.put("date", DateUtil.format(new Date(), DatePattern.NORM_DATE_FORMAT));
+        dataMap.put("commonExcludeFields", GeneratorConstant.COMMON_EXCLUDE_FIELDS);
+        dataMap.put("keywordQueryFields", GeneratorConstant.KEYWORD_QUERY_FIELDS);
+        dataMap.put("fillInsertFields", GeneratorConstant.FILL_INSERT_FIELDS);
+        dataMap.put("fillUpdateFields", GeneratorConstant.FILL_UPDATE_FIELDS);
+
+        // 是否存在BigDecimal、Date、Time类型
+        boolean existsBigDecimalType = false;
+        boolean existsDateType = false;
+        boolean existsTimeType = false;
+        boolean existsCreateTime = false;
+        boolean existsValidate = false;
+        for (GeneratorColumn column : columns) {
+            String propertyType = column.getPropertyType();
+            if (GeneratorConstant.JAVA_BIG_DECIMAL.equals(propertyType)) {
+                existsBigDecimalType = true;
+            } else if (GeneratorConstant.JAVA_DATE.equals(propertyType)) {
+                existsDateType = true;
+            } else if (GeneratorConstant.JAVA_TIME.equals(propertyType)) {
+                existsTimeType = true;
+            }
+            if (GeneratorConstant.CREATE_TIME_FIELD.equals(column.getPropertyName())) {
+                existsCreateTime = true;
+            }
+            if (column.getIsValidate()) {
+                existsValidate = true;
+            }
+        }
+        dataMap.put("existsBigDecimalType", existsBigDecimalType);
+        dataMap.put("existsDateType", existsDateType);
+        dataMap.put("existsTimeType", existsTimeType);
+        dataMap.put("existsCreateTime", existsCreateTime);
+        dataMap.put("existsValidate", existsValidate);
+
+        // 前端相关
+        String vueApiPath = String.format(GeneratorConstant.VUE_API_PATH, moduleName + "/" + entityObjectName);
+        dataMap.put("vueApiPath", vueApiPath);
+
+        // 设置菜单
+        // 目录菜单
+        SysMenu dirSysMenu = new SysMenu();
+        dirSysMenu.setId(IdUtil.getId());
+        dirSysMenu.setName(tableComment + GeneratorConstant.MANAGER_NAME);
+        if (parentMenuId == null) {
+            parentMenuId = 0L;
+        }
+        dirSysMenu.setParentId(parentMenuId);
+        dirSysMenu.setType(1);
+        dirSysMenu.setRouteUrl("/" + moduleName + entity);
+        // 菜单
+        SysMenu sysMenu = new SysMenu();
+        sysMenu.setId(IdUtil.getId());
+        sysMenu.setName(tableComment + GeneratorConstant.LIST_NAME);
+        sysMenu.setParentId(dirSysMenu.getId());
+        sysMenu.setType(2);
+        sysMenu.setCode(pagePermissionCode);
+        sysMenu.setRouteUrl("/" + moduleName + "/" + entityObjectName);
+        sysMenu.setComponentPath(moduleName + "/" + entityObjectName + GeneratorConstant.VUE_LIST_PATH);
+        // 菜单权限
+        List<SysMenu> permissionSysMenus = new ArrayList<>();
+        // 添加权限菜单
+        SysMenu addSysMenu = new SysMenu();
+        addSysMenu.setId(IdUtil.getId());
+        addSysMenu.setName(addCnComment);
+        addSysMenu.setParentId(sysMenu.getId());
+        addSysMenu.setType(3);
+        addSysMenu.setCode(addPermissionCode);
+        addSysMenu.setSort(1);
+        permissionSysMenus.add(addSysMenu);
+        // 修改权限菜单
+        SysMenu updateSysMenu = new SysMenu();
+        updateSysMenu.setId(IdUtil.getId());
+        updateSysMenu.setName(updateCnComment);
+        updateSysMenu.setParentId(sysMenu.getId());
+        updateSysMenu.setType(3);
+        updateSysMenu.setCode(updatePermissionCode);
+        updateSysMenu.setSort(2);
+        permissionSysMenus.add(updateSysMenu);
+        // 删除权限菜单
+        SysMenu deleteSysMenu = new SysMenu();
+        deleteSysMenu.setId(IdUtil.getId());
+        deleteSysMenu.setName(deleteCnComment);
+        deleteSysMenu.setParentId(sysMenu.getId());
+        deleteSysMenu.setType(3);
+        deleteSysMenu.setCode(deletePermissionCode);
+        deleteSysMenu.setSort(3);
+        permissionSysMenus.add(deleteSysMenu);
+        // 详情权限菜单
+        SysMenu infoSysMenu = new SysMenu();
+        infoSysMenu.setId(IdUtil.getId());
+        infoSysMenu.setName(infoCnComment);
+        infoSysMenu.setParentId(sysMenu.getId());
+        infoSysMenu.setType(3);
+        infoSysMenu.setCode(infoPermissionCode);
+        infoSysMenu.setSort(4);
+        permissionSysMenus.add(infoSysMenu);
+
+        dataMap.put("dirSysMenu", dirSysMenu);
+        dataMap.put("sysMenu", sysMenu);
+        dataMap.put("permissionSysMenus", permissionSysMenus);
+
+        return dataMap;
+    }
+
+
+    /**
+     * 获取生成后的文件
+     *
+     * @param dataMap
+     * @param templateName
+     */
+    public static void setGeneratorFile(GeneratorTable table, Map<String, Object> dataMap, String templateName, GeneratorCodeVo generatorCodeVo) {
+        String packageName = table.getPackageName();
+        String moduleName = table.getModuleName();
+        // 判断自定义后台路径为空或者为/，则输出到当前项目目录下，否则输出到指定项目目录下
+        String customBackendPath = table.getCustomBackendPath();
+        if (StringUtils.isBlank(customBackendPath) || "/".equals(customBackendPath)) {
+            // 写代码到当前项目路径下
+            customBackendPath = GeneratorConstant.USER_DIR;
+        }
+        if (customBackendPath.endsWith("/")) {
+            customBackendPath = customBackendPath.substring(0, customBackendPath.length() - 1);
+        }
+        // 判断自定义前端路径为空或者为/，则输出到当前项目vue目录下，否则输出到指定项目目录下
+        String customFrontendPath = table.getCustomFrontendPath();
+        if (StringUtils.isBlank(customFrontendPath) || "/".equals(customFrontendPath)) {
+            customFrontendPath = GeneratorConstant.USER_DIR + GeneratorConstant.VUE_DIR;
+        }
+        if (customFrontendPath.endsWith("/")) {
+            customFrontendPath = customFrontendPath.substring(0, customFrontendPath.length() - 1);
+        }
+        String packageFilePath = packageName.replaceAll("\\.", "/");
+        String javaModuleDir = GeneratorConstant.SRC_MAIN_JAVA + packageFilePath + "/" + moduleName + "/";
+        // java 自定义文件路径
+        String customJavaDir = customBackendPath + javaModuleDir;
+        // java zip下载路径
+        String zipJavaDir = GeneratorConstant.JAVA_DIR + javaModuleDir;
+        // xml文件路径
+        String mapperXmlDir = GeneratorConstant.SRC_MAIN_RESOURCES_MAPPER + moduleName + "/";
+        String customMapperXmlDir = customBackendPath + mapperXmlDir;
+        String zipMapperXmlDir = GeneratorConstant.JAVA_DIR + mapperXmlDir;
+        // 菜单SQL文件路径
+        String customMenuSqlDir = customBackendPath + GeneratorConstant.MENU_SQL_PATH;
+        String zipMenuSqlDir = GeneratorConstant.MENU_SQL_PATH;
+        // 实体对象名称
+        String entityObjectName = MapUtils.getString(dataMap, "entityObjectName");
+        // 前端API文件路径
+        String vueApiDir = GeneratorConstant.VUE_SRC_API + moduleName + "/";
+        String customVueApiDir = customFrontendPath + vueApiDir;
+        String zipVueApiDir = GeneratorConstant.VUE_DIR + vueApiDir;
+        // 前端页面文件路径
+        String vueViewDir = GeneratorConstant.VUE_SRC_VIEWS + moduleName + "/" + entityObjectName + "/";
+        String customVueViewDir = customFrontendPath + vueViewDir;
+        String zipVueViewDir = GeneratorConstant.VUE_DIR + vueViewDir;
+        String fileName = null;
+        String filePath = null;
+        String customFilePath = null;
+        String zipFilePath = null;
+        if (GeneratorConstant.APP_CONTROLLER_TEMPLATE_NAME.equals(templateName)) {
+            fileName = MapUtils.getString(dataMap, "appControllerName") + GeneratorConstant.DOT_JAVA;
+            filePath = GeneratorConstant.APP_CONTROLLER_PACKAGE_FILE_PATH + "/" + fileName;
+            customFilePath = customJavaDir + filePath;
+            zipFilePath = zipJavaDir + filePath;
+        } else if (GeneratorConstant.APP_QUERY_TEMPLATE_NAME.equals(templateName)) {
+            fileName = MapUtils.getString(dataMap, "appQueryName") + GeneratorConstant.DOT_JAVA;
+            filePath = GeneratorConstant.QUERY_PACKAGE + "/" + fileName;
+            customFilePath = customJavaDir + filePath;
+            zipFilePath = zipJavaDir + filePath;
+        } else if (GeneratorConstant.APP_VO_TEMPLATE_NAME.equals(templateName)) {
+            fileName = MapUtils.getString(dataMap, "appVoName") + GeneratorConstant.DOT_JAVA;
+            filePath = GeneratorConstant.VO_PACKAGE + "/" + fileName;
+            customFilePath = customJavaDir + filePath;
+            zipFilePath = zipJavaDir + filePath;
+        } else if (GeneratorConstant.CONTROLLER_TEMPLATE_NAME.equals(templateName)) {
+            fileName = MapUtils.getString(dataMap, "controllerName") + GeneratorConstant.DOT_JAVA;
+            filePath = GeneratorConstant.ADMIN_CONTROLLER_PACKAGE_FILE_PATH + "/" + fileName;
+            customFilePath = customJavaDir + filePath;
+            zipFilePath = zipJavaDir + filePath;
+        } else if (GeneratorConstant.DTO_TEMPLATE_NAME.equals(templateName)) {
+            fileName = MapUtils.getString(dataMap, "dtoName") + GeneratorConstant.DOT_JAVA;
+            filePath = GeneratorConstant.DTO_PACKAGE + "/" + fileName;
+            customFilePath = customJavaDir + filePath;
+            zipFilePath = zipJavaDir + filePath;
+        } else if (GeneratorConstant.ENTITY_TEMPLATE_NAME.equals(templateName)) {
+            fileName = MapUtils.getString(dataMap, "entity") + GeneratorConstant.DOT_JAVA;
+            filePath = GeneratorConstant.ENTITY_PACKAGE + "/" + fileName;
+            customFilePath = customJavaDir + filePath;
+            zipFilePath = zipJavaDir + filePath;
+        } else if (GeneratorConstant.MAPPER_TEMPLATE_NAME.equals(templateName)) {
+            fileName = MapUtils.getString(dataMap, "mapperName") + GeneratorConstant.DOT_JAVA;
+            filePath = GeneratorConstant.MAPPER_PACKAGE + "/" + fileName;
+            customFilePath = customJavaDir + filePath;
+            zipFilePath = zipJavaDir + filePath;
+        } else if (GeneratorConstant.MAPPER_XML_TEMPLATE_NAME.equals(templateName)) {
+            fileName = MapUtils.getString(dataMap, "mapperXmlName") + GeneratorConstant.DOT_XML;
+            customFilePath = customMapperXmlDir + fileName;
+            zipFilePath = zipMapperXmlDir + fileName;
+        } else if (GeneratorConstant.QUERY_TEMPLATE_NAME.equals(templateName)) {
+            fileName = MapUtils.getString(dataMap, "queryName") + GeneratorConstant.DOT_JAVA;
+            filePath = GeneratorConstant.QUERY_PACKAGE + "/" + fileName;
+            customFilePath = customJavaDir + filePath;
+            zipFilePath = zipJavaDir + filePath;
+        } else if (GeneratorConstant.SERVICE_TEMPLATE_NAME.equals(templateName)) {
+            fileName = MapUtils.getString(dataMap, "serviceName") + GeneratorConstant.DOT_JAVA;
+            filePath = GeneratorConstant.SERVICE_PACKAGE + "/" + fileName;
+            customFilePath = customJavaDir + filePath;
+            zipFilePath = zipJavaDir + filePath;
+        } else if (GeneratorConstant.SERVICE_IMPL_TEMPLATE_NAME.equals(templateName)) {
+            fileName = MapUtils.getString(dataMap, "serviceImplName") + GeneratorConstant.DOT_JAVA;
+            filePath = GeneratorConstant.SERVICE_IMPL_PACKAGE_FILE_PATH + "/" + fileName;
+            customFilePath = customJavaDir + filePath;
+            zipFilePath = zipJavaDir + filePath;
+        } else if (GeneratorConstant.VO_TEMPLATE_NAME.equals(templateName)) {
+            fileName = MapUtils.getString(dataMap, "voName") + GeneratorConstant.DOT_JAVA;
+            filePath = GeneratorConstant.VO_PACKAGE + "/" + fileName;
+            customFilePath = customJavaDir + filePath;
+            zipFilePath = zipJavaDir + filePath;
+        } else if (GeneratorConstant.MENU_SQL_TEMPLATE_NAME.equals(templateName)) {
+            fileName = String.format(GeneratorConstant.MENU_SQL_FORMAT_FILE_NAME, table.getTableName());
+            customFilePath = customMenuSqlDir + fileName;
+            zipFilePath = zipMenuSqlDir + fileName;
+        } else if (GeneratorConstant.API_TS_TEMPLATE_NAME.equals(templateName)) {
+            fileName = String.format(GeneratorConstant.API_TS_FORMAT_FILE_NAME, entityObjectName);
+            customFilePath = customVueApiDir + fileName;
+            zipFilePath = zipVueApiDir + fileName;
+        } else if (GeneratorConstant.VUE_INDEX_TEMPLATE_NAME.equals(templateName)) {
+            fileName = GeneratorConstant.VUE_INDEX_FILE_NAME;
+            customFilePath = customVueViewDir + fileName;
+            zipFilePath = zipVueViewDir + fileName;
+        } else if (GeneratorConstant.VUE_FORM_TEMPLATE_NAME.equals(templateName)) {
+            fileName = GeneratorConstant.VUE_FORM_FILE_NAME;
+            customFilePath = customVueViewDir + fileName;
+            zipFilePath = zipVueViewDir + fileName;
+        }
+        generatorCodeVo.setFileName(fileName);
+        generatorCodeVo.setCustomFilePath(customFilePath);
+        generatorCodeVo.setZipFilePath(zipFilePath);
+    }
+
+    /**
+     * 处理请求映射
+     *
+     * @param requestMappingStyle
+     * @param dataMap
+     * @param pkIdPropertyName
+     * @param entity
+     * @param entityObjectName
+     */
+    public static void handleRequestMapping(Integer requestMappingStyle, Map<String, Object> dataMap, String pkIdPropertyName, String entity, String entityObjectName) {
+        if (RequestMappingStyle.DEFAULT.getCode().equals(requestMappingStyle)) {
+            // 普通方法，默认风格
+            setDefaultRequestMappingStyle(dataMap, pkIdPropertyName, entity, entityObjectName);
+        } else if (RequestMappingStyle.RESTFUL.getCode().equals(requestMappingStyle)) {
+            // restful
+            setRestfulRequestMappingStyle(dataMap, pkIdPropertyName, entity, entityObjectName);
+        } else if (RequestMappingStyle.SMALL_LETTER.getCode().equals(requestMappingStyle)) {
+            // 全部字母小写
+            setSmallLetterRequestMappingStyle(dataMap, pkIdPropertyName, entity, entityObjectName);
+        } else if (RequestMappingStyle.HYPHEN.getCode().equals(requestMappingStyle)) {
+            // 中横线连接
+            setHyphenRequestMappingStyle(dataMap, pkIdPropertyName, entity, entityObjectName);
+        } else if (RequestMappingStyle.UNDERLINE.getCode().equals(requestMappingStyle)) {
+            // 下划线连接
+            setUnderlineRequestMappingStyle(dataMap, pkIdPropertyName, entity, entityObjectName);
+        }
+    }
+
+    /**
+     * 设置默认风格的请求路径映射方式
+     *
+     * @param dataMap
+     * @param pkIdPropertyName
+     * @param entity
+     * @param entityObjectName
+     */
+    public static void setDefaultRequestMappingStyle(Map<String, Object> dataMap, String pkIdPropertyName, String entity, String entityObjectName) {
+        String controllerRequestPath = GeneratorConstant.ADMIN_MAPPING + "/" + entityObjectName;
+        String controllerRequestMapping = String.format(GeneratorConstant.REQUEST_MAPPING_FORMAT, controllerRequestPath);
+        String appControllerRequestPath = GeneratorConstant.APP_MAPPING + "/" + entityObjectName;
+        String appControllerRequestMapping = String.format(GeneratorConstant.REQUEST_MAPPING_FORMAT, appControllerRequestPath);
+        String addRequestPath = "/" + GeneratorConstant.ADD + entity;
+        String addRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, addRequestPath);
+        String updateRequestPath = "/" + GeneratorConstant.UPDATE + entity;
+        String updateRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, updateRequestPath);
+        String deleteRequestPath = "/" + GeneratorConstant.DELETE + entity + "/";
+        String deleteRequestFormatPath = deleteRequestPath + "{" + pkIdPropertyName + "}";
+        String deleteRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, deleteRequestFormatPath);
+        String infoRequestPath = "/" + GeneratorConstant.GET + entity + "/";
+        String infoRequestFormatPath = infoRequestPath + "{" + pkIdPropertyName + "}";
+        String infoRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, infoRequestFormatPath);
+        String pageRequestPath = "/" + GeneratorConstant.GET + entity + GeneratorConstant.PAGE;
+        String pageRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, pageRequestPath);
+        String appInfoRequestPath = "/" + GeneratorConstant.GET + GeneratorConstant.APP_PREFIX + entity + "/{" + pkIdPropertyName + "}";
+        String appInfoRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, appInfoRequestPath);
+        String appPageRequestPath = "/" + GeneratorConstant.GET + GeneratorConstant.APP_PREFIX + entity + GeneratorConstant.PAGE;
+        String appPageRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, appPageRequestPath);
+        String pageParamAnnotation = GeneratorConstant.REQUEST_BODY;
+        dataMap.put("controllerRequestMapping", controllerRequestMapping);
+        dataMap.put("appControllerRequestMapping", appControllerRequestMapping);
+        dataMap.put("addRequestMapping", addRequestMapping);
+        dataMap.put("updateRequestMapping", updateRequestMapping);
+        dataMap.put("deleteRequestMapping", deleteRequestMapping);
+        dataMap.put("infoRequestMapping", infoRequestMapping);
+        dataMap.put("pageRequestMapping", pageRequestMapping);
+        dataMap.put("appInfoRequestMapping", appInfoRequestMapping);
+        dataMap.put("appPageRequestMapping", appPageRequestMapping);
+        dataMap.put("pageParamAnnotation", pageParamAnnotation);
+
+        dataMap.put("controllerRequestPath", controllerRequestPath);
+        dataMap.put("appControllerRequestPath", appControllerRequestPath);
+        dataMap.put("addRequestPath", addRequestPath);
+        dataMap.put("updateRequestPath", updateRequestPath);
+        dataMap.put("deleteRequestPath", deleteRequestPath);
+        dataMap.put("infoRequestPath", infoRequestPath);
+        dataMap.put("pageRequestPath", pageRequestPath);
+        dataMap.put("appInfoRequestPath", appInfoRequestPath);
+        dataMap.put("appPageRequestPath", appPageRequestPath);
+    }
+
+    /**
+     * 设置restful风格的请求路径映射方式
+     *
+     * @param dataMap
+     * @param pkIdPropertyName
+     * @param entity
+     * @param entityObjectName
+     */
+    public static void setRestfulRequestMappingStyle(Map<String, Object> dataMap, String pkIdPropertyName, String entity, String entityObjectName) {
+        String entityMapping = GeneratorUtil.camelToBackslash(entityObjectName);
+        String controllerRequestMapping = GeneratorConstant.ADMIN_MAPPING + "/" + entityMapping;
+        controllerRequestMapping = String.format(GeneratorConstant.REQUEST_MAPPING_FORMAT, controllerRequestMapping);
+        String appControllerRequestMapping = GeneratorConstant.APP_MAPPING + "/" + entityMapping;
+        appControllerRequestMapping = String.format(GeneratorConstant.REQUEST_MAPPING_FORMAT, appControllerRequestMapping);
+        String addRequestMapping = GeneratorConstant.POST_MAPPING;
+        String updateRequestMapping = GeneratorConstant.PUT_MAPPING;
+        String deleteRequestMapping = "/{" + pkIdPropertyName + "}";
+        deleteRequestMapping = String.format(GeneratorConstant.DELETE_MAPPING_FORMAT, deleteRequestMapping);
+        String infoRequestMapping = "/{" + pkIdPropertyName + "}";
+        infoRequestMapping = String.format(GeneratorConstant.GET_MAPPING_FORMAT, infoRequestMapping);
+        String pageRequestMapping = "/" + GeneratorConstant.LOWER_PAGE;
+        pageRequestMapping = String.format(GeneratorConstant.GET_MAPPING_FORMAT, pageRequestMapping);
+        String appInfoRequestMapping = "/{" + pkIdPropertyName + "}";
+        appInfoRequestMapping = String.format(GeneratorConstant.GET_MAPPING_FORMAT, appInfoRequestMapping);
+        String appPageRequestMapping = "/" + GeneratorConstant.LOWER_PAGE;
+        appPageRequestMapping = String.format(GeneratorConstant.GET_MAPPING_FORMAT, appPageRequestMapping);
+        String pageParamAnnotation = GeneratorConstant.PARAMETER_OBJECT;
+        dataMap.put("controllerRequestMapping", controllerRequestMapping);
+        dataMap.put("appControllerRequestMapping", appControllerRequestMapping);
+        dataMap.put("addRequestMapping", addRequestMapping);
+        dataMap.put("updateRequestMapping", updateRequestMapping);
+        dataMap.put("deleteRequestMapping", deleteRequestMapping);
+        dataMap.put("infoRequestMapping", infoRequestMapping);
+        dataMap.put("pageRequestMapping", pageRequestMapping);
+        dataMap.put("appInfoRequestMapping", appInfoRequestMapping);
+        dataMap.put("appPageRequestMapping", appPageRequestMapping);
+        dataMap.put("pageParamAnnotation", pageParamAnnotation);
+    }
+
+    /**
+     * 设置全部字母小写风格的请求路径映射方式
+     *
+     * @param dataMap
+     * @param pkIdPropertyName
+     * @param entity
+     * @param entityObjectName
+     */
+    public static void setSmallLetterRequestMappingStyle(Map<String, Object> dataMap, String pkIdPropertyName, String entity, String entityObjectName) {
+        String controllerRequestMapping = GeneratorConstant.ADMIN_MAPPING + "/" + entityObjectName;
+        controllerRequestMapping = GeneratorUtil.toLower(controllerRequestMapping);
+        controllerRequestMapping = String.format(GeneratorConstant.REQUEST_MAPPING_FORMAT, controllerRequestMapping);
+        String appControllerRequestMapping = GeneratorConstant.APP_MAPPING + "/" + entityObjectName;
+        appControllerRequestMapping = GeneratorUtil.toLower(appControllerRequestMapping);
+        appControllerRequestMapping = String.format(GeneratorConstant.REQUEST_MAPPING_FORMAT, appControllerRequestMapping);
+        String addRequestMapping = "/" + GeneratorConstant.ADD + entity;
+        addRequestMapping = GeneratorUtil.toLower(addRequestMapping);
+        addRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, addRequestMapping);
+        String updateRequestMapping = "/" + GeneratorConstant.UPDATE + entity;
+        updateRequestMapping = GeneratorUtil.toLower(updateRequestMapping);
+        updateRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, updateRequestMapping);
+        String deleteRequestMapping = "/" + GeneratorConstant.DELETE + entity + "/{" + pkIdPropertyName + "}";
+        deleteRequestMapping = GeneratorUtil.toLower(deleteRequestMapping);
+        deleteRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, deleteRequestMapping);
+        String infoRequestMapping = "/" + GeneratorUtil.toLower(GeneratorConstant.GET + entity) + "/{" + pkIdPropertyName + "}";
+        infoRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, infoRequestMapping);
+        String pageRequestMapping = "/" + GeneratorConstant.GET + entity + GeneratorConstant.PAGE;
+        pageRequestMapping = GeneratorUtil.toLower(pageRequestMapping);
+        pageRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, pageRequestMapping);
+        String appInfoRequestMapping = "/" + GeneratorUtil.toLower(GeneratorConstant.GET + GeneratorConstant.APP_PREFIX + entity) + "/{" + pkIdPropertyName + "}";
+        appInfoRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, appInfoRequestMapping);
+        String appPageRequestMapping = "/" + GeneratorConstant.GET + GeneratorConstant.APP_PREFIX + entity + GeneratorConstant.PAGE;
+        appPageRequestMapping = GeneratorUtil.toLower(appPageRequestMapping);
+        appPageRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, appPageRequestMapping);
+        String pageParamAnnotation = GeneratorConstant.REQUEST_BODY;
+        dataMap.put("controllerRequestMapping", controllerRequestMapping);
+        dataMap.put("appControllerRequestMapping", appControllerRequestMapping);
+        dataMap.put("addRequestMapping", addRequestMapping);
+        dataMap.put("updateRequestMapping", updateRequestMapping);
+        dataMap.put("deleteRequestMapping", deleteRequestMapping);
+        dataMap.put("infoRequestMapping", infoRequestMapping);
+        dataMap.put("pageRequestMapping", pageRequestMapping);
+        dataMap.put("appInfoRequestMapping", appInfoRequestMapping);
+        dataMap.put("appPageRequestMapping", appPageRequestMapping);
+        dataMap.put("pageParamAnnotation", pageParamAnnotation);
+    }
+
+    /**
+     * 设置中横线风格的请求路径映射方式
+     *
+     * @param dataMap
+     * @param pkIdPropertyName
+     * @param entity
+     * @param entityObjectName
+     */
+    public static void setHyphenRequestMappingStyle(Map<String, Object> dataMap, String pkIdPropertyName, String entity, String entityObjectName) {
+        String controllerRequestMapping = GeneratorConstant.ADMIN_MAPPING + "/" + entityObjectName;
+        controllerRequestMapping = GeneratorUtil.toHyphen(controllerRequestMapping);
+        controllerRequestMapping = String.format(GeneratorConstant.REQUEST_MAPPING_FORMAT, controllerRequestMapping);
+        String appControllerRequestMapping = GeneratorConstant.APP_MAPPING + "/" + entityObjectName;
+        appControllerRequestMapping = GeneratorUtil.toHyphen(appControllerRequestMapping);
+        appControllerRequestMapping = String.format(GeneratorConstant.REQUEST_MAPPING_FORMAT, appControllerRequestMapping);
+        String addRequestMapping = "/" + GeneratorConstant.ADD + entity;
+        addRequestMapping = GeneratorUtil.toHyphen(addRequestMapping);
+        addRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, addRequestMapping);
+        String updateRequestMapping = "/" + GeneratorConstant.UPDATE + entity;
+        updateRequestMapping = GeneratorUtil.toHyphen(updateRequestMapping);
+        updateRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, updateRequestMapping);
+        String deleteRequestMapping = "/" + GeneratorConstant.DELETE + entity + "/{" + pkIdPropertyName + "}";
+        deleteRequestMapping = GeneratorUtil.toHyphen(deleteRequestMapping);
+        deleteRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, deleteRequestMapping);
+        String infoRequestMapping = "/" + GeneratorUtil.toHyphen(GeneratorConstant.GET + entity) + "/{" + pkIdPropertyName + "}";
+        infoRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, infoRequestMapping);
+        String pageRequestMapping = "/" + GeneratorConstant.GET + entity + GeneratorConstant.PAGE;
+        pageRequestMapping = GeneratorUtil.toHyphen(pageRequestMapping);
+        pageRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, pageRequestMapping);
+        String appInfoRequestMapping = "/" + GeneratorUtil.toHyphen(GeneratorConstant.GET + GeneratorConstant.APP_PREFIX + entity) + "/{" + pkIdPropertyName + "}";
+        appInfoRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, appInfoRequestMapping);
+        String appPageRequestMapping = "/" + GeneratorConstant.GET + GeneratorConstant.APP_PREFIX + entity + GeneratorConstant.PAGE;
+        appPageRequestMapping = GeneratorUtil.toHyphen(appPageRequestMapping);
+        appPageRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, appPageRequestMapping);
+        String pageParamAnnotation = GeneratorConstant.REQUEST_BODY;
+        dataMap.put("controllerRequestMapping", controllerRequestMapping);
+        dataMap.put("appControllerRequestMapping", appControllerRequestMapping);
+        dataMap.put("addRequestMapping", addRequestMapping);
+        dataMap.put("updateRequestMapping", updateRequestMapping);
+        dataMap.put("deleteRequestMapping", deleteRequestMapping);
+        dataMap.put("infoRequestMapping", infoRequestMapping);
+        dataMap.put("pageRequestMapping", pageRequestMapping);
+        dataMap.put("appInfoRequestMapping", appInfoRequestMapping);
+        dataMap.put("appPageRequestMapping", appPageRequestMapping);
+        dataMap.put("pageParamAnnotation", pageParamAnnotation);
+    }
+
+    /**
+     * 设置下划线风格的请求路径映射方式
+     *
+     * @param dataMap
+     * @param pkIdPropertyName
+     * @param entity
+     * @param entityObjectName
+     */
+    public static void setUnderlineRequestMappingStyle(Map<String, Object> dataMap, String pkIdPropertyName, String entity, String entityObjectName) {
+        String controllerRequestMapping = GeneratorConstant.ADMIN_MAPPING + "/" + entityObjectName;
+        controllerRequestMapping = GeneratorUtil.toUnderline(controllerRequestMapping);
+        controllerRequestMapping = String.format(GeneratorConstant.REQUEST_MAPPING_FORMAT, controllerRequestMapping);
+        String appControllerRequestMapping = GeneratorConstant.APP_MAPPING + "/" + entityObjectName;
+        appControllerRequestMapping = GeneratorUtil.toUnderline(appControllerRequestMapping);
+        appControllerRequestMapping = String.format(GeneratorConstant.REQUEST_MAPPING_FORMAT, appControllerRequestMapping);
+        String addRequestMapping = "/" + GeneratorConstant.ADD + entity;
+        addRequestMapping = GeneratorUtil.toUnderline(addRequestMapping);
+        addRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, addRequestMapping);
+        String updateRequestMapping = "/" + GeneratorConstant.UPDATE + entity;
+        updateRequestMapping = GeneratorUtil.toUnderline(updateRequestMapping);
+        updateRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, updateRequestMapping);
+        String deleteRequestMapping = "/" + GeneratorConstant.DELETE + entity + "/{" + pkIdPropertyName + "}";
+        deleteRequestMapping = GeneratorUtil.toUnderline(deleteRequestMapping);
+        deleteRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, deleteRequestMapping);
+        String infoRequestMapping = "/" + GeneratorUtil.toUnderline(GeneratorConstant.GET + entity) + "/{" + pkIdPropertyName + "}";
+        infoRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, infoRequestMapping);
+        String pageRequestMapping = "/" + GeneratorConstant.GET + entity + GeneratorConstant.PAGE;
+        pageRequestMapping = GeneratorUtil.toUnderline(pageRequestMapping);
+        pageRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, pageRequestMapping);
+        String appInfoRequestMapping = "/" + GeneratorUtil.toUnderline(GeneratorConstant.GET + GeneratorConstant.APP_PREFIX + entity) + "/{" + pkIdPropertyName + "}";
+        appInfoRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, appInfoRequestMapping);
+        String appPageRequestMapping = "/" + GeneratorConstant.GET + GeneratorConstant.APP_PREFIX + entity + GeneratorConstant.PAGE;
+        appPageRequestMapping = GeneratorUtil.toUnderline(appPageRequestMapping);
+        appPageRequestMapping = String.format(GeneratorConstant.POST_MAPPING_FORMAT, appPageRequestMapping);
+        String pageParamAnnotation = GeneratorConstant.REQUEST_BODY;
+        dataMap.put("controllerRequestMapping", controllerRequestMapping);
+        dataMap.put("appControllerRequestMapping", appControllerRequestMapping);
+        dataMap.put("addRequestMapping", addRequestMapping);
+        dataMap.put("updateRequestMapping", updateRequestMapping);
+        dataMap.put("deleteRequestMapping", deleteRequestMapping);
+        dataMap.put("infoRequestMapping", infoRequestMapping);
+        dataMap.put("pageRequestMapping", pageRequestMapping);
+        dataMap.put("appInfoRequestMapping", appInfoRequestMapping);
+        dataMap.put("appPageRequestMapping", appPageRequestMapping);
+        dataMap.put("pageParamAnnotation", pageParamAnnotation);
+    }
+
+    /**
+     * 获取属性类型
+     *
+     * @param dataType
+     * @param columnLength
+     * @return
+     */
+    public static String getPropertyType(String dataType, Integer columnLength) {
+        // 转换类型
+        columnLength = columnLength == null ? 0 : columnLength;
+        if (GeneratorConstant.DB_TINYINT.equals(dataType) && columnLength == 1) {
+            // tinyint(1)设置为Boolean类型
+            return GeneratorConstant.JAVA_BOOLEAN;
+        } else {
+            return getPropertyType(dataType);
+        }
+    }
+
+    /**
+     * 获取属性类型
+     *
+     * @param dataType
+     * @return
+     */
+    public static String getPropertyType(String dataType) {
+        return COLUMN_TYPE_MAPPING_MAP.get(dataType).getPropertyType();
+    }
+
+    /**
+     * 获取OptionJson
+     *
+     * @param columnComment
+     * @param columnCustomComment
+     * @param dataType
+     * @return
+     */
+    public static String getOptionJson(String columnComment, String columnCustomComment, String dataType) {
+        if (GeneratorConstant.DB_INT_LIST.contains(dataType)) {
+            // 枚举类型json
+            List<GeneratorOptionVo> autoColumnOptions = GeneratorUtil.getAutoColumnOptions(columnComment, columnCustomComment);
+            if (CollectionUtils.isNotEmpty(autoColumnOptions)) {
+                String optionJson = JSON.toJSONString(autoColumnOptions);
+                return optionJson;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 下划线专程驼峰命名
+     * sys_user --> sysUser
+     *
+     * @param underline
+     * @return
+     */
+    public static String underlineToCamel(String underline) {
+        if (StringUtils.isNotBlank(underline)) {
+            return NamingStrategy.underlineToCamel(underline);
+        }
+        return null;
+    }
+
+    /**
+     * 下划线转换成帕斯卡命名
+     * sys_user --> SysUser
+     *
+     * @param underline
+     * @return
+     */
+    public static String underlineToPascal(String underline) {
+        if (StringUtils.isNotBlank(underline)) {
+            return NamingStrategy.capitalFirst(NamingStrategy.underlineToCamel(underline));
+        }
+        return null;
+    }
+
+    /**
+     * 骆驼转换成帕斯卡命名
+     * roleId --> RoleId
+     *
+     * @param camel
+     * @return
+     */
+    public static String camelToPascal(String camel) {
+        if (StringUtils.isNotBlank(camel)) {
+            return NamingStrategy.capitalFirst(camel);
+        }
+        return null;
+    }
+
+    /**
+     * 下划线转换成冒号连接命名
+     * sys_user --> sys:user
+     *
+     * @param underline
+     * @return
+     */
+    public static String underlineToColon(String underline) {
+        if (StringUtils.isNotBlank(underline)) {
+            String string = underline.toLowerCase();
+            return string.replaceAll("_", ":");
+        }
+        return null;
+    }
+
+    /**
+     * 帕斯卡转换成驼峰命名法
+     * SysUser --> sysUser
+     *
+     * @param pascal
+     * @return
+     */
+    public static String pascalToCamel(String pascal) {
+        if (StringUtils.isNotBlank(pascal)) {
+            return pascal.substring(0, 1).toLowerCase() + pascal.substring(1);
+        }
+        return pascal;
+    }
+
+    /**
+     * 下划线转换成横线连接命名
+     * sys_user --> sys-user
+     *
+     * @param underline
+     * @return
+     */
+    public static String underlineToHyphen(String underline) {
+        if (StringUtils.isNotBlank(underline)) {
+            String string = underline.toLowerCase();
+            return string.replaceAll("_", "-");
+        }
+        return null;
+    }
+
+    /**
+     * 转换成中横线连接
+     * sys-user
+     *
+     * @param value
+     * @return
+     */
+    public static String toHyphen(String value) {
+        if (StringUtils.isNotBlank(value)) {
+            String result = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, value);
+            return result.replaceAll("_", "-");
+        }
+        return null;
+    }
+
+    /**
+     * 转换成下划线
+     *
+     * @param value
+     * @return
+     */
+    public static String toUnderline(String value) {
+        if (StringUtils.isNotBlank(value)) {
+            return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, value);
+        }
+        return null;
+    }
+
+    /**
+     * 转换成反斜杠
+     *
+     * @param value
+     * @return
+     */
+    public static String toBackslash(String value) {
+        if (StringUtils.isNotBlank(value)) {
+            String result = toHyphen(value);
+            return result.replaceAll("-", "/");
+        }
+        return null;
+    }
+
+    /**
+     * 驼峰转换成反斜杠
+     * sysUser --> sys/user
+     *
+     * @param value
+     * @return
+     */
+    public static String camelToBackslash(String value) {
+        if (StringUtils.isNotBlank(value)) {
+            String result = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, value);
+            return result.replaceAll("-", "/");
+        }
+        return null;
+    }
+
+    /**
+     * 转换成小写
+     *
+     * @param value
+     * @return
+     */
+    public static String toLower(String value) {
+        if (StringUtils.isNotBlank(value)) {
+            return value.toLowerCase();
+        }
+        return null;
+    }
+
+    /**
+     * 获取没有前缀的表对应的类名称
+     *
+     * @param tableName
+     * @return
+     */
+    public static String getTableClassName(String tableName, List<String> tablePrefixes) {
+        String notPrefixTableName = tableName;
+        if (CollectionUtils.isNotEmpty(tablePrefixes)) {
+            for (String tablePrefix : tablePrefixes) {
+                if (tableName.startsWith(tablePrefix)) {
+                    notPrefixTableName = tableName.substring(tablePrefix.length());
+                    break;
+                }
+            }
+        }
+        // 类名称 默认将表名称转换成帕斯卡命名发
+        String className = underlineToPascal(notPrefixTableName);
+        return className;
+    }
+
+    /**
+     * 获取列长度
+     *
+     * @param columnType
+     * @return
+     */
+    public static Integer getColumnLength(String columnType) {
+        int index = columnType.indexOf("(");
+        int lastIndex = columnType.lastIndexOf(")");
+        if (index != -1) {
+            String substring = columnType.substring(index + 1, lastIndex);
+            int i = substring.indexOf(",");
+            if (i != -1) {
+                substring = substring.substring(0, i);
+            }
+            return Integer.parseInt(substring);
+        }
+        return null;
+    }
+
+    /**
+     * 获取处理后的表注释
+     *
+     * @param tableComment
+     * @return
+     */
+    public static String getTableComment(String tableComment) {
+        if (StringUtils.isBlank(tableComment)) {
+            return null;
+        }
+        for (String removeTableNameSuffix : GeneratorConstant.REMOVE_TABLE_NAME_SUFFIXES) {
+            if (tableComment.endsWith(removeTableNameSuffix)) {
+                return tableComment.substring(0, tableComment.length() - 1);
+            }
+        }
+        return tableComment;
+    }
+
+    /**
+     * 获取注释前半部分
+     *
+     * @param columnComment
+     * @return
+     */
+    public static String getColumnCustomComment(String columnComment) {
+        if (StringUtils.isBlank(columnComment)) {
+            return null;
+        }
+        int index = columnComment.indexOf(" ");
+        if (index != -1) {
+            return columnComment.substring(0, index);
+        }
+        index = columnComment.indexOf("，");
+        if (index != -1) {
+            return columnComment.substring(0, index);
+        }
+        index = columnComment.indexOf(",");
+        if (index != -1) {
+            return columnComment.substring(0, index);
+        }
+        return columnComment;
+    }
+
+    /**
+     * 获取注释后半部分
+     *
+     * @param columnComment
+     * @param columnCustomComment
+     * @return
+     */
+    public static List<GeneratorOptionVo> getAutoColumnOptions(String columnComment, String columnCustomComment) {
+        try {
+            if (StringUtils.isBlank(columnComment)) {
+                return null;
+            }
+            String commentOption = null;
+            if (StringUtils.isNotBlank(columnCustomComment)) {
+                int columnCommentLength = columnComment.length();
+                int columnCustomCommentLength = columnCustomComment.length();
+                if (columnCommentLength > columnCustomCommentLength + 1) {
+                    commentOption = columnComment.substring(columnCustomComment.length() + 1, columnCommentLength);
+                }
+            }
+            if (StringUtils.isBlank(commentOption)) {
+                return null;
+            }
+            // 按逗号拆分
+            commentOption = commentOption.replaceAll("，", ",");
+            commentOption = commentOption.replaceAll("，", ",");
+            commentOption = commentOption.replaceAll(";", ",");
+            commentOption = commentOption.replaceAll("；", ",");
+            commentOption = commentOption.replaceAll("\\(", "");
+            commentOption = commentOption.replaceAll("\\（", "");
+            commentOption = commentOption.replaceAll("\\)", "");
+            commentOption = commentOption.replaceAll("\\）", "");
+            List<GeneratorOptionVo> optionVos = new ArrayList<>();
+            int index = commentOption.indexOf(",");
+            if (index != -1) {
+                autoSplitOptions(",", commentOption, optionVos);
+            } else {
+                // 按空格拆分
+                autoSplitOptions(" ", commentOption, optionVos);
+            }
+            return optionVos;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 自动拆分option
+     *
+     * @param splitSymbol
+     * @param commentOption
+     * @param optionVos
+     */
+    private static void autoSplitOptions(String splitSymbol, String commentOption, List<GeneratorOptionVo> optionVos) {
+        int index;
+        String[] strings = commentOption.split(splitSymbol);
+        if (ArrayUtils.isNotEmpty(strings) && strings.length > 1) {
+            for (String string : strings) {
+                string = string.replaceAll("：", ":");
+                string = string.replaceAll("-", ":");
+                string = string.replaceAll(" ", ":");
+                index = string.indexOf(":");
+                if (index != -1) {
+                    String[] optionArray = string.split(":");
+                    if (ArrayUtils.isNotEmpty(optionArray) && optionArray.length == 2) {
+                        String value = optionArray[0].trim();
+                        String label = optionArray[1].trim();
+                        GeneratorOptionVo optionVo = new GeneratorOptionVo();
+                        optionVo.setValue(value);
+                        optionVo.setLabel(label);
+                        optionVos.add(optionVo);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 获取后端模板列表
+     *
+     * @return
+     * @throws Exception
+     */
+    public static Map<String, String> getBackendEntityTemplateMap() {
+        return BACKEND_ENTITY_TEMPLATE_MAP;
+    }
+
+    /**
+     * 获取后端模板列表
+     *
+     * @return
+     * @throws Exception
+     */
+    public static Map<String, String> getBackendTemplateMap() {
+        return BACKEND_TEMPLATE_MAP;
+    }
+
+    /**
+     * 获取App后端模板列表
+     *
+     * @return
+     * @throws Exception
+     */
+    public static Map<String, String> getAppBackendTemplateMap() {
+        return APP_BACKEND_TEMPLATE_MAP;
+    }
+
+    /**
+     * 获取前端模板列表
+     *
+     * @return
+     * @throws Exception
+     */
+    public static Map<String, String> getFrontendTemplateMap() {
+        return FRONTEND_TEMPLATE_MAP;
+    }
+
+
+    /**
+     * 获取菜单SQL模板列表
+     *
+     * @return
+     * @throws Exception
+     */
+    public static Map<String, String> getMenuTemplateMap() {
+        return MENU_TEMPLATE_MAP;
+    }
+
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/util/VelocityUtil.java b/src/main/java/io/geekidea/boot/generator/util/VelocityUtil.java
new file mode 100644
index 00000000..419d8780
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/util/VelocityUtil.java
@@ -0,0 +1,55 @@
+package io.geekidea.boot.generator.util;
+
+import com.baomidou.mybatisplus.generator.config.ConstVal;
+import io.geekidea.boot.generator.constant.GeneratorConstant;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
+
+import java.io.StringWriter;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author geekidea
+ * @date 2023/12/31
+ **/
+@Slf4j
+public class VelocityUtil {
+
+    private static Properties properties;
+
+    static {
+        properties = new Properties();
+        properties.setProperty("resource.loader.file.class", ClasspathResourceLoader.class.getName());
+        properties.setProperty("resource.loader.file.unicode", "true");
+        properties.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, "");
+        properties.setProperty(Velocity.ENCODING_DEFAULT, GeneratorConstant.UTF8);
+        properties.setProperty(Velocity.INPUT_ENCODING, GeneratorConstant.UTF8);
+
+    }
+
+    /**
+     * 渲染模板数据输出到StringWrite中
+     *
+     * @param templatePath
+     * @param dataMap
+     * @return
+     * @throws Exception
+     */
+    public static String writer(String templatePath, Map<String, Object> dataMap) {
+        VelocityEngine velocityEngine = new VelocityEngine(properties);
+        StringWriter writer = new StringWriter();
+        Template template = velocityEngine.getTemplate(templatePath, ConstVal.UTF8);
+        template.merge(new VelocityContext(dataMap), writer);
+        String content = writer.toString();
+        IOUtils.closeQuietly(writer);
+        return content;
+    }
+
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/vo/GeneratorCodeVo.java b/src/main/java/io/geekidea/boot/generator/vo/GeneratorCodeVo.java
new file mode 100644
index 00000000..729d5e69
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/vo/GeneratorCodeVo.java
@@ -0,0 +1,31 @@
+package io.geekidea.boot.generator.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 生成代码Vo
+ *
+ * @author geekidea
+ * @date 2023/12/30
+ **/
+@Data
+@Schema(description = "生成代码Vo")
+public class GeneratorCodeVo {
+
+    @Schema(description = "文件名称")
+    private String fileName;
+
+    @Schema(description = "自定义文件路径")
+    private String customFilePath;
+
+    @Schema(description = "zip文件路径")
+    private String zipFilePath;
+
+    @Schema(description = "文件内容")
+    private String fileContent;
+
+    @Schema(description = "生成代码的模板类型 1：后台代码，2：前端代码，3：菜单SQL")
+    private Integer templateType;
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/vo/GeneratorColumnDbVo.java b/src/main/java/io/geekidea/boot/generator/vo/GeneratorColumnDbVo.java
new file mode 100644
index 00000000..785b39ec
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/vo/GeneratorColumnDbVo.java
@@ -0,0 +1,48 @@
+package io.geekidea.boot.generator.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 数据库中列信息Vo
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Data
+@Schema(description = "数据库中列信息Vo")
+public class GeneratorColumnDbVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "表名称")
+    private String tableName;
+
+    @Schema(description = "字段名称")
+    private String columnName;
+
+    @Schema(description = "字段注释")
+    private String columnComment;
+
+    @Schema(description = "数据类型")
+    private String dataType;
+
+    @Schema(description = "字段类型")
+    private String columnType;
+
+    @Schema(description = "列顺序" )
+    private Integer columnSort;
+
+    @Schema(description = "是否主键")
+    private Boolean isPk;
+
+    @Schema(description = "是否必填")
+    private Boolean isRequired;
+
+    @Schema(description = "是否有默认值")
+    private Boolean isDefaultValue;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/generator/vo/GeneratorOptionVo.java b/src/main/java/io/geekidea/boot/generator/vo/GeneratorOptionVo.java
new file mode 100644
index 00000000..763e5d67
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/vo/GeneratorOptionVo.java
@@ -0,0 +1,18 @@
+package io.geekidea.boot.generator.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * @author geekidea
+ * @date 2024/1/3
+ **/
+@Data
+@Schema(description = "生成代码optionVo")
+public class GeneratorOptionVo {
+
+    private String label;
+
+    private String value;
+
+}
diff --git a/src/main/java/io/geekidea/boot/generator/vo/GeneratorTableDbVo.java b/src/main/java/io/geekidea/boot/generator/vo/GeneratorTableDbVo.java
new file mode 100644
index 00000000..031711ed
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/generator/vo/GeneratorTableDbVo.java
@@ -0,0 +1,40 @@
+package io.geekidea.boot.generator.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 数据库中的表Vo
+ *
+ * @author geekidea
+ * @since 2023-12-29
+ */
+@Data
+@Schema(description = "数据库中的表Vo" )
+public class GeneratorTableDbVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "表名称" )
+    private String tableName;
+
+    @Schema(description = "表注释" )
+    private String tableComment;
+
+    @Schema(description = "创建时间" )
+    private Date createTime;
+
+    @Schema(description = "类名称")
+    private String className;
+
+    @Schema(description = "最近生成的时间")
+    private Date updateTime;
+
+    @Schema(description = "生成方式 1：zip压缩包，2：自定义路径")
+    private Integer generatorType;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/controller/ServerInfoController.java b/src/main/java/io/geekidea/boot/system/controller/ServerInfoController.java
new file mode 100644
index 00000000..1a241c01
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/controller/ServerInfoController.java
@@ -0,0 +1,65 @@
+package io.geekidea.boot.system.controller;
+
+import cn.hutool.system.SystemUtil;
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.framework.bean.ProjectInfo;
+import io.geekidea.boot.framework.bean.ServerInfo;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.util.ServerInfoUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author geekidea
+ * @date 2023/12/16
+ **/
+@Slf4j
+@RestController
+@Tag(name = "服务信息")
+@RequestMapping("/admin/serverInfo")
+public class ServerInfoController {
+
+    @Autowired
+    private Environment environment;
+
+    /**
+     * 获取服务信息详情
+     *
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getServerInfo")
+    @Operation(summary = "服务信息详情")
+    @Permission("server:info")
+    public ApiResult<ServerInfo> getServerInfo() {
+        ServerInfo serverInfo = ServerInfoUtil.getServerInfo();
+        // 获取项目信息
+        // 项目名称
+        String name = environment.getProperty("spring.application.name");
+        // 端口号
+        String port = environment.getProperty("server.port");
+        // 上下文路径
+        String contextPath = environment.getProperty("server.servlet.context-path");
+        // 激活的环境
+        String active = environment.getProperty("spring.profiles.active");
+        // 当前项目路径
+        String userDir = SystemUtil.get("user.dir");
+        ProjectInfo projectInfo = new ProjectInfo();
+        projectInfo.setName(name);
+        projectInfo.setPort(port);
+        projectInfo.setContextPath(contextPath);
+        projectInfo.setActive(active);
+        projectInfo.setUserDir(userDir);
+        serverInfo.setProjectInfo(projectInfo);
+        log.info("serverInfo:" + serverInfo);
+        return ApiResult.success(serverInfo);
+    }
+
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/controller/SysConfigController.java b/src/main/java/io/geekidea/boot/system/controller/SysConfigController.java
new file mode 100644
index 00000000..8f37420a
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/controller/SysConfigController.java
@@ -0,0 +1,113 @@
+package io.geekidea.boot.system.controller;
+
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.common.enums.SysLogType;
+import io.geekidea.boot.framework.annotation.Log;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.system.dto.SysConfigDto;
+import io.geekidea.boot.system.query.SysConfigQuery;
+import io.geekidea.boot.system.service.SysConfigService;
+import io.geekidea.boot.system.vo.SysConfigVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+/**
+ * 系统配置 控制器
+ *
+ * @author geekidea
+ * @since 2023-11-27
+ */
+@Slf4j
+@RestController
+@Tag(name = "系统配置")
+@RequestMapping("/admin/sysConfig")
+public class SysConfigController {
+
+    @Autowired
+    private SysConfigService sysConfigService;
+
+    /**
+     * 添加系统配置
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "添加系统配置", type = SysLogType.ADD)
+    @PostMapping("/addSysConfig")
+    @Operation(summary = "添加系统配置")
+    @Permission("sys:config:add")
+    public ApiResult addSysConfig(@Valid @RequestBody SysConfigDto dto) {
+        boolean flag = sysConfigService.addSysConfig(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 修改系统配置
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "修改系统配置", type = SysLogType.UPDATE)
+    @PostMapping("/updateSysConfig")
+    @Operation(summary = "修改系统配置")
+    @Permission("sys:config:update")
+    public ApiResult updateSysConfig(@Valid @RequestBody SysConfigDto dto) {
+        boolean flag = sysConfigService.updateSysConfig(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 删除系统配置
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "删除系统配置", type = SysLogType.DELETE)
+    @PostMapping("/deleteSysConfig/{id}")
+    @Operation(summary = "删除系统配置")
+    @Permission("sys:config:delete")
+    public ApiResult deleteSysConfig(@PathVariable Long id) {
+        boolean flag = sysConfigService.deleteSysConfig(id);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 获取系统配置详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysConfig/{id}")
+    @Operation(summary = "系统配置详情")
+    @Permission("sys:config:info")
+    public ApiResult<SysConfigVo> getSysConfig(@PathVariable Long id) {
+        SysConfigVo sysConfigVo = sysConfigService.getSysConfigById(id);
+        return ApiResult.success(sysConfigVo);
+    }
+
+    /**
+     * 系统配置分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysConfigPage")
+    @Operation(summary = "系统配置分页列表")
+    @Permission("sys:config:page")
+    public ApiResult<SysConfigVo> getSysConfigPage(@Valid @RequestBody SysConfigQuery query) {
+        Paging<SysConfigVo> paging = sysConfigService.getSysConfigPage(query);
+        return ApiResult.success(paging);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/controller/SysDictAppController.java b/src/main/java/io/geekidea/boot/system/controller/SysDictAppController.java
new file mode 100644
index 00000000..b5318f78
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/controller/SysDictAppController.java
@@ -0,0 +1,49 @@
+package io.geekidea.boot.system.controller;
+
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.system.query.SysDictAppQuery;
+import io.geekidea.boot.system.service.SysDictService;
+import io.geekidea.boot.system.vo.AppSysDictVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+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 javax.validation.Valid;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * App字典数据 控制器
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Slf4j
+@RestController
+@Tag(name = "App字典数据")
+@RequestMapping("/app/sysDict")
+public class SysDictAppController {
+
+    @Autowired
+    private SysDictService sysDictService;
+
+    /**
+     * App字典数据列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getAppSysDictList")
+    @Operation(summary = "App字典数据列表")
+    public ApiResult<AppSysDictVo> getAppSysDictList(@Valid @RequestBody SysDictAppQuery query) {
+        Map<String, List<AppSysDictVo>> map = sysDictService.getAppSysDictList(query);
+        return ApiResult.success(map);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/controller/SysDictController.java b/src/main/java/io/geekidea/boot/system/controller/SysDictController.java
new file mode 100644
index 00000000..4ebbfaa2
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/controller/SysDictController.java
@@ -0,0 +1,113 @@
+package io.geekidea.boot.system.controller;
+
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.common.enums.SysLogType;
+import io.geekidea.boot.framework.annotation.Log;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.system.dto.SysDictDto;
+import io.geekidea.boot.system.query.SysDictQuery;
+import io.geekidea.boot.system.service.SysDictService;
+import io.geekidea.boot.system.vo.SysDictVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+/**
+ * 字典数据 控制器
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Slf4j
+@RestController
+@Tag(name = "字典数据")
+@RequestMapping("/admin/sysDict")
+public class SysDictController {
+
+    @Autowired
+    private SysDictService sysDictService;
+
+    /**
+     * 添加字典数据
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "添加字典数据", type = SysLogType.ADD)
+    @PostMapping("/addSysDict")
+    @Operation(summary = "添加字典数据")
+    @Permission("sys:dict:add")
+    public ApiResult addSysDict(@Valid @RequestBody SysDictDto dto) {
+        boolean flag = sysDictService.addSysDict(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 修改字典数据
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "修改字典数据", type = SysLogType.UPDATE)
+    @PostMapping("/updateSysDict")
+    @Operation(summary = "修改字典数据")
+    @Permission("sys:dict:update")
+    public ApiResult updateSysDict(@Valid @RequestBody SysDictDto dto) {
+        boolean flag = sysDictService.updateSysDict(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 删除字典数据
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "删除字典数据", type = SysLogType.DELETE)
+    @PostMapping("/deleteSysDict/{id}")
+    @Operation(summary = "删除字典数据")
+    @Permission("sys:dict:delete")
+    public ApiResult deleteSysDict(@PathVariable Long id) {
+        boolean flag = sysDictService.deleteSysDict(id);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 获取字典数据详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysDict/{id}")
+    @Operation(summary = "字典数据详情")
+    @Permission("sys:dict:info")
+    public ApiResult<SysDictVo> getSysDict(@PathVariable Long id) {
+        SysDictVo sysDictVo = sysDictService.getSysDictById(id);
+        return ApiResult.success(sysDictVo);
+    }
+
+    /**
+     * 字典数据分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysDictPage")
+    @Operation(summary = "字典数据分页列表")
+    @Permission("sys:dict:page")
+    public ApiResult<SysDictVo> getSysDictPage(@Valid @RequestBody SysDictQuery query) {
+        Paging<SysDictVo> paging = sysDictService.getSysDictPage(query);
+        return ApiResult.success(paging);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/controller/SysDictTypeController.java b/src/main/java/io/geekidea/boot/system/controller/SysDictTypeController.java
new file mode 100644
index 00000000..5e1a7a6d
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/controller/SysDictTypeController.java
@@ -0,0 +1,113 @@
+package io.geekidea.boot.system.controller;
+
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.common.enums.SysLogType;
+import io.geekidea.boot.framework.annotation.Log;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.system.dto.SysDictTypeDto;
+import io.geekidea.boot.system.query.SysDictTypeQuery;
+import io.geekidea.boot.system.service.SysDictTypeService;
+import io.geekidea.boot.system.vo.SysDictTypeVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 字典类型 控制器
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Slf4j
+@RestController
+@Tag(name = "字典类型")
+@RequestMapping("/admin/sysDictType")
+public class SysDictTypeController {
+
+    @Autowired
+    private SysDictTypeService sysDictTypeService;
+
+    /**
+     * 添加字典类型
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "添加字典类型", type = SysLogType.ADD)
+    @PostMapping("/addSysDictType")
+    @Operation(summary = "添加字典类型")
+    @Permission("sys:dict:type:add")
+    public ApiResult addSysDictType(@Valid @RequestBody SysDictTypeDto dto) {
+        boolean flag = sysDictTypeService.addSysDictType(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 修改字典类型
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "修改字典类型", type = SysLogType.UPDATE)
+    @PostMapping("/updateSysDictType")
+    @Operation(summary = "修改字典类型")
+    @Permission("sys:dict:type:update")
+    public ApiResult updateSysDictType(@Valid @RequestBody SysDictTypeDto dto) {
+        boolean flag = sysDictTypeService.updateSysDictType(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 删除字典类型
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "删除字典类型", type = SysLogType.DELETE)
+    @PostMapping("/deleteSysDictType/{id}")
+    @Operation(summary = "删除字典类型")
+    @Permission("sys:dict:type:delete")
+    public ApiResult deleteSysDictType(@PathVariable Long id) {
+        boolean flag = sysDictTypeService.deleteSysDictType(id);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 获取字典类型详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysDictType/{id}")
+    @Operation(summary = "字典类型详情")
+    @Permission("sys:dict:type:info")
+    public ApiResult<SysDictTypeVo> getSysDictType(@PathVariable Long id) {
+        SysDictTypeVo sysDictTypeVo = sysDictTypeService.getSysDictTypeById(id);
+        return ApiResult.success(sysDictTypeVo);
+    }
+
+    /**
+     * 字典类型列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysDictTypeList")
+    @Operation(summary = "字典类型列表")
+    @Permission("sys:dict:type:list")
+    public ApiResult<SysDictTypeVo> getSysDictTypeList(@Valid @RequestBody SysDictTypeQuery query) {
+        List<SysDictTypeVo> list = sysDictTypeService.getSysDictTypeList(query);
+        return ApiResult.success(list);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/controller/SysFileController.java b/src/main/java/io/geekidea/boot/system/controller/SysFileController.java
new file mode 100644
index 00000000..b50ad035
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/controller/SysFileController.java
@@ -0,0 +1,97 @@
+package io.geekidea.boot.system.controller;
+
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.common.enums.SysLogType;
+import io.geekidea.boot.framework.annotation.Log;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.system.dto.SysFileDto;
+import io.geekidea.boot.system.query.SysFileQuery;
+import io.geekidea.boot.system.service.SysFileService;
+import io.geekidea.boot.system.vo.SysFileVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+/**
+ * 系统文件 控制器
+ *
+ * @author geekidea
+ * @since 2023-11-26
+ */
+@Slf4j
+@RestController
+@Tag(name = "系统文件")
+@RequestMapping("/admin/sysFile")
+public class SysFileController {
+
+    @Autowired
+    private SysFileService sysFileService;
+
+    /**
+     * 修改系统文件
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "修改系统文件", type = SysLogType.UPDATE)
+    @PostMapping("/updateSysFile")
+    @Operation(summary = "修改系统文件")
+    @Permission("sys:file:update")
+    public ApiResult updateSysFile(@Valid @RequestBody SysFileDto dto) {
+        boolean flag = sysFileService.updateSysFile(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 删除系统文件
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "删除系统文件", type = SysLogType.DELETE)
+    @PostMapping("/deleteSysFile/{id}")
+    @Operation(summary = "删除系统文件")
+    @Permission("sys:file:delete")
+    public ApiResult deleteSysFile(@PathVariable Long id) {
+        boolean flag = sysFileService.deleteSysFile(id);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 获取系统文件详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysFile/{id}")
+    @Operation(summary = "系统文件详情")
+    @Permission("sys:file:info")
+    public ApiResult<SysFileVo> getSysFile(@PathVariable Long id) {
+        SysFileVo sysFileVo = sysFileService.getSysFileById(id);
+        return ApiResult.success(sysFileVo);
+    }
+
+    /**
+     * 系统文件分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysFilePage")
+    @Operation(summary = "系统文件分页列表")
+    @Permission("sys:file:page")
+    public ApiResult<SysFileVo> getSysFilePage(@Valid @RequestBody SysFileQuery query) {
+        Paging<SysFileVo> paging = sysFileService.getSysFilePage(query);
+        return ApiResult.success(paging);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/controller/SysLogController.java b/src/main/java/io/geekidea/boot/system/controller/SysLogController.java
new file mode 100644
index 00000000..ccd72848
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/controller/SysLogController.java
@@ -0,0 +1,62 @@
+package io.geekidea.boot.system.controller;
+
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.system.query.SysLogQuery;
+import io.geekidea.boot.system.service.SysLogService;
+import io.geekidea.boot.system.vo.SysLogVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+/**
+ * 系统日志 控制器
+ *
+ * @author geekidea
+ * @since 2023-02-16
+ */
+@Slf4j
+@RestController
+@Tag(name = "系统日志")
+@RequestMapping("/admin/sysLog")
+public class SysLogController {
+
+    @Autowired
+    private SysLogService sysLogService;
+
+    /**
+     * 获取系统日志详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysLog/{id}")
+    @Operation(summary = "系统日志详情")
+    @Permission("sys:log:info")
+    public ApiResult<SysLogVo> getSysLog(@PathVariable Long id) {
+        SysLogVo sysLogVo = sysLogService.getSysLogById(id);
+        return ApiResult.success(sysLogVo);
+    }
+
+    /**
+     * 系统日志分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysLogPage")
+    @Operation(summary = "系统日志分页列表")
+    @Permission("sys:log:page")
+    public ApiResult<SysLogVo> getSysLogPage(@Valid @RequestBody SysLogQuery query) {
+        Paging<SysLogVo> paging = sysLogService.getSysLogPage(query);
+        return ApiResult.success(paging);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/controller/SysMenuController.java b/src/main/java/io/geekidea/boot/system/controller/SysMenuController.java
new file mode 100644
index 00000000..1ed9e54f
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/controller/SysMenuController.java
@@ -0,0 +1,136 @@
+package io.geekidea.boot.system.controller;
+
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.system.dto.SysMenuDto;
+import io.geekidea.boot.system.query.SysMenuQuery;
+import io.geekidea.boot.system.service.SysMenuService;
+import io.geekidea.boot.system.vo.SysMenuTreeVo;
+import io.geekidea.boot.system.vo.SysMenuVo;
+import io.geekidea.boot.system.vo.SysNavMenuTreeVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 系统菜单 控制器
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Slf4j
+@RestController
+@Tag(name = "系统菜单")
+@RequestMapping("/admin/sysMenu")
+public class SysMenuController {
+
+    @Autowired
+    private SysMenuService sysMenuService;
+
+    /**
+     * 添加系统菜单
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/addSysMenu")
+    @Operation(summary = "添加系统菜单")
+    @Permission("sys:menu:add")
+    public ApiResult addSysMenu(@Valid @RequestBody SysMenuDto dto) {
+        boolean flag = sysMenuService.addSysMenu(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 修改系统菜单
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/updateSysMenu")
+    @Operation(summary = "修改系统菜单")
+    @Permission("sys:menu:update")
+    public ApiResult updateSysMenu(@Valid @RequestBody SysMenuDto dto) {
+        boolean flag = sysMenuService.updateSysMenu(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 删除系统菜单
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/deleteSysMenu/{id}")
+    @Operation(summary = "删除系统菜单")
+    @Permission("sys:menu:delete")
+    public ApiResult deleteSysMenu(@PathVariable Long id) {
+        boolean flag = sysMenuService.deleteSysMenu(id);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 获取系统菜单详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysMenu/{id}")
+    @Operation(summary = "系统菜单详情")
+    @Permission("sys:menu:info")
+    public ApiResult<SysMenuVo> getSysMenu(@PathVariable Long id) {
+        SysMenuVo sysMenuVo = sysMenuService.getSysMenuById(id);
+        return ApiResult.success(sysMenuVo);
+    }
+
+    /**
+     * 获取所有的系统菜单树形列表
+     *
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getAllSysMenuTreeList")
+    @Operation(summary = "获取所有的系统菜单树形列表")
+    @Permission("sys:menu:all-tree-list")
+    public ApiResult<SysMenuTreeVo> getAllSysMenuTreeList(@Valid @RequestBody SysMenuQuery query) {
+        List<SysMenuTreeVo> list = sysMenuService.getAllSysMenuTreeList(query);
+        return ApiResult.success(list);
+    }
+
+    /**
+     * 获取启用的系统菜单树形列表
+     *
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysMenuTreeList")
+    @Operation(summary = "获取启用的系统菜单树形列表")
+    @Permission("sys:menu:tree-list")
+    public ApiResult<SysMenuTreeVo> getSysMenuTreeList() {
+        List<SysMenuTreeVo> list = sysMenuService.getSysMenuTreeList();
+        return ApiResult.success(list);
+    }
+
+    /**
+     * 获取当前用户的导航菜单
+     *
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getNavMenuTreeList")
+    @Operation(summary = "获取当前用户的导航菜单")
+    public ApiResult<SysNavMenuTreeVo> getNavMenuTreeList() {
+        List<SysNavMenuTreeVo> list = sysMenuService.getNavMenuTreeList();
+        return ApiResult.success(list);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/controller/SysRoleController.java b/src/main/java/io/geekidea/boot/system/controller/SysRoleController.java
new file mode 100644
index 00000000..f4d6a2a4
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/controller/SysRoleController.java
@@ -0,0 +1,155 @@
+package io.geekidea.boot.system.controller;
+
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.system.dto.RoleMenusDto;
+import io.geekidea.boot.system.dto.SysRoleDto;
+import io.geekidea.boot.system.entity.SysRole;
+import io.geekidea.boot.system.query.SysRoleQuery;
+import io.geekidea.boot.system.service.SysMenuService;
+import io.geekidea.boot.system.service.SysRoleService;
+import io.geekidea.boot.system.vo.SysRoleVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 系统角色 控制器
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Slf4j
+@RestController
+@Tag(name = "系统角色")
+@RequestMapping("/admin/sysRole")
+public class SysRoleController {
+
+    @Autowired
+    private SysRoleService sysRoleService;
+
+    @Autowired
+    private SysMenuService sysMenuService;
+
+    /**
+     * 添加系统角色
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/addSysRole")
+    @Operation(summary = "添加系统角色")
+    @Permission("sys:role:add")
+    public ApiResult addSysRole(@Valid @RequestBody SysRoleDto dto) {
+        boolean flag = sysRoleService.addSysRole(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 修改系统角色
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/updateSysRole")
+    @Operation(summary = "修改系统角色")
+    @Permission("sys:role:update")
+    public ApiResult updateSysRole(@Valid @RequestBody SysRoleDto dto) {
+        boolean flag = sysRoleService.updateSysRole(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 删除系统角色
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/deleteSysRole/{id}")
+    @Operation(summary = "删除系统角色")
+    @Permission("sys:role:delete")
+    public ApiResult deleteSysRole(@PathVariable Long id) {
+        boolean flag = sysRoleService.deleteSysRole(id);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 获取系统角色详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysRole/{id}")
+    @Operation(summary = "系统角色详情")
+    @Permission("sys:role:info")
+    public ApiResult<SysRoleVo> getSysRole(@PathVariable Long id) {
+        SysRoleVo sysRoleVo = sysRoleService.getSysRoleById(id);
+        return ApiResult.success(sysRoleVo);
+    }
+
+    /**
+     * 系统角色分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysRolePage")
+    @Operation(summary = "系统角色分页列表")
+    @Permission("sys:role:page")
+    public ApiResult<SysRoleVo> getSysRolePage(@Valid @RequestBody SysRoleQuery query) {
+        Paging<SysRoleVo> paging = sysRoleService.getSysRolePage(query);
+        return ApiResult.success(paging);
+    }
+
+    /**
+     * 系统所有角色列表
+     *
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysRoleAllList")
+    @Operation(summary = "系统所有角色列表")
+    public ApiResult<SysRole> getSysRoleAllList() {
+        List<SysRole> list = sysRoleService.getSysRoleAllList();
+        return ApiResult.success(list);
+    }
+
+    /**
+     * 设置角色权限
+     *
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/setRoleMenus")
+    @Operation(summary = "设置角色权限")
+    @Permission("sys:role:set-role-menus")
+    public ApiResult setRoleMenus(@Valid @RequestBody RoleMenusDto roleMenusDto) {
+        boolean flag = sysRoleService.setRoleMenus(roleMenusDto);
+        return ApiResult.success(flag);
+    }
+
+    /**
+     * 获取角色权限ID集合
+     *
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getMenuIdsByRoleId/{roleId}")
+    @Operation(summary = "获取角色权限ID集合")
+    public ApiResult getMenuIdsByRoleId(@PathVariable Long roleId) {
+        List<Long> list = sysMenuService.getMenuIdsByRoleId(roleId);
+        return ApiResult.success(list);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/controller/SysUserController.java b/src/main/java/io/geekidea/boot/system/controller/SysUserController.java
new file mode 100644
index 00000000..ffd02a11
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/controller/SysUserController.java
@@ -0,0 +1,188 @@
+package io.geekidea.boot.system.controller;
+
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.auth.util.LoginUtil;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.system.dto.SysUserDto;
+import io.geekidea.boot.system.dto.SysUserResetPasswordDto;
+import io.geekidea.boot.system.dto.SysUserUpdatePasswordDto;
+import io.geekidea.boot.system.dto.SysUserUpdateProfileDto;
+import io.geekidea.boot.system.query.SysUserQuery;
+import io.geekidea.boot.system.service.SysUserService;
+import io.geekidea.boot.system.vo.SysUserVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.Valid;
+
+/**
+ * 系统用户 控制器
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Slf4j
+@RestController
+@Tag(name = "系统用户")
+@RequestMapping("/admin/sysUser")
+public class SysUserController {
+
+    @Autowired
+    private SysUserService sysUserService;
+
+    /**
+     * 添加系统用户
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/addSysUser")
+    @Operation(summary = "添加系统用户")
+    @Permission("sys:user:add")
+    public ApiResult addSysUser(@Valid @RequestBody SysUserDto dto) {
+        boolean flag = sysUserService.addSysUser(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 修改系统用户
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/updateSysUser")
+    @Operation(summary = "修改系统用户")
+    @Permission("sys:user:update")
+    public ApiResult updateSysUser(@Valid @RequestBody SysUserDto dto) {
+        boolean flag = sysUserService.updateSysUser(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 删除系统用户
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/deleteSysUser/{id}")
+    @Operation(summary = "删除系统用户")
+    @Permission("sys:user:delete")
+    public ApiResult deleteSysUser(@PathVariable Long id) {
+        boolean flag = sysUserService.deleteSysUser(id);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 获取系统用户详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysUser/{id}")
+    @Operation(summary = "系统用户详情")
+    @Permission("sys:user:info")
+    public ApiResult<SysUserVo> getSysUser(@PathVariable Long id) {
+        SysUserVo sysUserVo = sysUserService.getSysUserById(id);
+        return ApiResult.success(sysUserVo);
+    }
+
+    /**
+     * 系统用户分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getSysUserPage")
+    @Operation(summary = "系统用户分页列表")
+    @Permission("sys:user:page")
+    public ApiResult<SysUserVo> getSysUserPage(@Valid @RequestBody SysUserQuery query) {
+        Paging<SysUserVo> paging = sysUserService.getSysUserPage(query);
+        return ApiResult.success(paging);
+    }
+
+    /**
+     * 重置系统用户密码
+     *
+     * @param sysUserResetPasswordDto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/resetSysUserPassword")
+    @Operation(summary = "重置系统用户密码")
+    @Permission("sys:user:reset-password")
+    public ApiResult resetSysUserPassword(@Valid @RequestBody SysUserResetPasswordDto sysUserResetPasswordDto) {
+        boolean flag = sysUserService.resetSysUserPassword(sysUserResetPasswordDto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 获取个人信息
+     *
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getProfile")
+    @Operation(summary = "获取个人信息")
+    public ApiResult<SysUserVo> getProfile() {
+        Long userId = LoginUtil.getUserId();
+        if (userId == null) {
+            throw new BusinessException("用户ID为空");
+        }
+        SysUserVo sysUserVo = sysUserService.getSysUserById(userId);
+        return ApiResult.success(sysUserVo);
+    }
+
+    /**
+     * 修改个人信息
+     *
+     * @param sysUserUpdateProfileDto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/updateProfile")
+    @Operation(summary = "修改个人信息")
+    public ApiResult updateProfile(@Valid @RequestBody SysUserUpdateProfileDto sysUserUpdateProfileDto) {
+        boolean flag = sysUserService.updateProfile(sysUserUpdateProfileDto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 修改用户密码
+     *
+     * @param sysUserUpdatePasswordDto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/updatePassword")
+    @Operation(summary = "修改用户密码")
+    public ApiResult updatePassword(@Valid @RequestBody SysUserUpdatePasswordDto sysUserUpdatePasswordDto) {
+        boolean flag = sysUserService.updatePassword(sysUserUpdatePasswordDto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 导入Excel用户数据
+     *
+     * @param multipartFile
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/importExcel")
+    @Operation(summary = "导入Excel用户数据")
+    public ApiResult importExcel(MultipartFile multipartFile) throws Exception {
+        boolean flag = sysUserService.importExcel(multipartFile);
+        return ApiResult.result(flag);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/dto/RoleMenusDto.java b/src/main/java/io/geekidea/boot/system/dto/RoleMenusDto.java
new file mode 100644
index 00000000..11102d93
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/dto/RoleMenusDto.java
@@ -0,0 +1,25 @@
+package io.geekidea.boot.system.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2023/6/18
+ **/
+@Data
+@Schema(description = "设置角色权限Dto")
+public class RoleMenusDto {
+
+    @Schema(description = "角色ID")
+    @NotNull(message = "角色ID不能为空")
+    private Long roleId;
+
+    @Schema(description = "菜单ID集合")
+    @NotNull(message = "菜单ID集合不能为空")
+    private List<Long> menuIds;
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/dto/SysConfigDto.java b/src/main/java/io/geekidea/boot/system/dto/SysConfigDto.java
new file mode 100644
index 00000000..8f246e4b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/dto/SysConfigDto.java
@@ -0,0 +1,55 @@
+package io.geekidea.boot.system.dto;
+
+import java.util.Date;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 修改系统配置参数
+ *
+ * @author geekidea
+ * @since 2023-11-27
+ */
+@Data
+@Schema(description = "系统配置参数")
+public class SysConfigDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "配置名称")
+    @NotBlank(message = "配置名称不能为空")
+    @Length(max = 100, message = "配置名称长度超过限制")
+    private String configName;
+
+    @Schema(description = "配置key")
+    @NotBlank(message = "配置key不能为空")
+    @Length(max = 100, message = "配置key长度超过限制")
+    private String configKey;
+
+    @Schema(description = "配置值")
+    @NotBlank(message = "配置值不能为空")
+    @Length(max = 200, message = "配置值长度超过限制")
+    private String configValue;
+
+    @Schema(description = "是否是系统字典类型")
+    private Boolean isSystem;
+
+    @Schema(description = "状态 1：正常，0：禁用")
+    private Boolean status;
+
+    @Schema(description = "备注")
+    @Length(max = 200, message = "备注长度超过限制")
+    private String remark;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/system/dto/SysDictDto.java b/src/main/java/io/geekidea/boot/system/dto/SysDictDto.java
new file mode 100644
index 00000000..6c521d6e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/dto/SysDictDto.java
@@ -0,0 +1,54 @@
+package io.geekidea.boot.system.dto;
+
+import java.util.Date;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 修改字典数据参数
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "修改字典数据参数")
+public class SysDictDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "字典值")
+    @NotBlank(message = "字典值不能为空")
+    @Length(max = 200, message = "字典值长度超过限制")
+    private String value;
+
+    @Schema(description = "字典名称")
+    @NotBlank(message = "字典名称不能为空")
+    @Length(max = 100, message = "字典名称长度超过限制")
+    private String label;
+
+    @Schema(description = "字典类型code")
+    @NotBlank(message = "字典类型code不能为空")
+    @Length(max = 100, message = "字典类型code长度超过限制")
+    private String dictCode;
+
+    @Schema(description = "状态 1：启用，0：禁用")
+    private Boolean status;
+
+    @Schema(description = "排序值")
+    private Integer sort;
+
+    @Schema(description = "备注")
+    private String remark;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/system/dto/SysDictTypeDto.java b/src/main/java/io/geekidea/boot/system/dto/SysDictTypeDto.java
new file mode 100644
index 00000000..82e7d674
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/dto/SysDictTypeDto.java
@@ -0,0 +1,46 @@
+package io.geekidea.boot.system.dto;
+
+import java.util.Date;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 修改字典类型参数
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "字典类型参数")
+public class SysDictTypeDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "字典类型编码")
+    @NotBlank(message = "字典类型编码不能为空")
+    @Length(max = 100, message = "字典类型编码长度超过限制")
+    private String code;
+
+    @Schema(description = "字典类型名称")
+    @NotBlank(message = "字典类型名称不能为空")
+    @Length(max = 100, message = "字典类型名称长度超过限制")
+    private String name;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "是否是系统字典类型")
+    private Boolean isSystem;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/system/dto/SysFileDto.java b/src/main/java/io/geekidea/boot/system/dto/SysFileDto.java
new file mode 100644
index 00000000..4143a3bd
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/dto/SysFileDto.java
@@ -0,0 +1,31 @@
+package io.geekidea.boot.system.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 修改系统文件参数
+ *
+ * @author geekidea
+ * @since 2023-11-26
+ */
+@Data
+@Schema(description = "系统文件参数")
+public class SysFileDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "源文件名称")
+    @Length(max = 200, message = "源文件名称长度超过限制")
+    private String originalFileName;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/system/dto/SysMenuDto.java b/src/main/java/io/geekidea/boot/system/dto/SysMenuDto.java
new file mode 100644
index 00000000..0044db90
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/dto/SysMenuDto.java
@@ -0,0 +1,70 @@
+package io.geekidea.boot.system.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 修改系统菜单参数
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@Schema(description = "系统菜单参数")
+public class SysMenuDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "菜单名称")
+    @Length(max = 32, message = "菜单名称长度超过限制")
+    private String name;
+
+    @Schema(description = "父id")
+    private Long parentId;
+
+    @Schema(description = "菜单编码")
+    @Length(max = 100, message = "菜单编码长度超过限制")
+    private String code;
+
+    @Schema(description = "菜单图标")
+    @Length(max = 100, message = "菜单图标长度超过限制")
+    private String icon;
+
+    @Schema(description = "菜单类型，1：目录，2：菜单，3：权限")
+    @NotNull(message = "菜单类型不能为空")
+    private Integer type;
+
+    @Schema(description = "状态，0：禁用，1：启用")
+    private Boolean status;
+
+    @Schema(description = "排序")
+    private Integer sort;
+
+    @Schema(description = "前端路由地址")
+    @Length(max = 200, message = "前端路由地址长度超过限制")
+    private String routeUrl;
+
+    @Schema(description = "重定向")
+    @Length(max = 100, message = "重定向长度超过限制")
+    private String routeRedirect;
+
+    @Schema(description = "组件路径")
+    @Length(max = 100, message = "组件路径长度超过限制")
+    private String componentPath;
+
+    @Schema(description = "是否显示,0：不显示，1：显示")
+    private Boolean isShow;
+
+    @Schema(description = "是否缓存，0：否 1：是")
+    private Boolean isCache;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/system/dto/SysRoleDto.java b/src/main/java/io/geekidea/boot/system/dto/SysRoleDto.java
new file mode 100644
index 00000000..16b0655e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/dto/SysRoleDto.java
@@ -0,0 +1,43 @@
+package io.geekidea.boot.system.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 修改系统角色参数
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@Schema(description = "系统角色参数")
+public class SysRoleDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "角色名称")
+    @NotBlank(message = "角色名称不能为空")
+    @Length(max = 32, message = "角色名称长度超过限制")
+    private String name;
+
+    @Schema(description = "角色唯一编码")
+    @Length(max = 100, message = "角色唯一编码长度超过限制")
+    private String code;
+
+    @Schema(description = "是否系统内置角色 1：是，0：否")
+    private Boolean isSystem;
+
+    @Schema(description = "角色备注")
+    @Length(max = 200, message = "角色备注长度超过限制")
+    private String remark;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/system/dto/SysUserDto.java b/src/main/java/io/geekidea/boot/system/dto/SysUserDto.java
new file mode 100644
index 00000000..28264e0f
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/dto/SysUserDto.java
@@ -0,0 +1,55 @@
+package io.geekidea.boot.system.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 修改系统用户参数
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@Schema(description = "系统用户参数")
+public class SysUserDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "用户名")
+    @NotBlank(message = "用户名不能为空")
+    @Length(max = 20, message = "用户名长度超过限制")
+    private String username;
+
+    @Schema(description = "昵称")
+    @NotBlank(message = "昵称不能为空")
+    @Length(max = 20, message = "昵称长度超过限制")
+    private String nickname;
+
+    @Schema(description = "密码")
+    private String password;
+
+    @Schema(description = "手机号码")
+    @Length(max = 11, message = "手机号码长度超过限制")
+    private String phone;
+
+    @Schema(description = "电子邮件")
+    @Length(max = 255, message = "电子邮件长度超过限制")
+    private String email;
+
+    @Schema(description = "状态，0：禁用，1：启用")
+    private Boolean status;
+
+    @Schema(description = "角色id")
+    private Long roleId;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/system/dto/SysUserExcelDto.java b/src/main/java/io/geekidea/boot/system/dto/SysUserExcelDto.java
new file mode 100644
index 00000000..25c85c1c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/dto/SysUserExcelDto.java
@@ -0,0 +1,44 @@
+package io.geekidea.boot.system.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+/**
+ * 系统用户Excel导入参数映射
+ *
+ * @author geekidea
+ * @since 2023-12-25
+ */
+@Data
+@Schema(description = "系统用户Excel导入参数映射")
+public class SysUserExcelDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "用户名")
+    private String username;
+
+    @Schema(description = "昵称")
+    private String nickname;
+
+    @Schema(description = "密码")
+    private String password;
+
+    @Schema(description = "手机号码")
+    private String phone;
+
+    @Schema(description = "电子邮件")
+    private String email;
+
+    @Schema(description = "状态，0：禁用，1：启用")
+    private Boolean status;
+
+    @Schema(description = "角色id")
+    private Long roleId;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/system/dto/SysUserResetPasswordDto.java b/src/main/java/io/geekidea/boot/system/dto/SysUserResetPasswordDto.java
new file mode 100644
index 00000000..0acf22f7
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/dto/SysUserResetPasswordDto.java
@@ -0,0 +1,28 @@
+package io.geekidea.boot.system.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 管理员重置系统用户密码
+ *
+ * @author geekidea
+ * @date 2023/6/18
+ **/
+@Data
+@Schema(description = "管理员重置系统用户密码" )
+public class SysUserResetPasswordDto {
+
+    @Schema(description = "用户ID" )
+    @NotNull(message = "用户ID不能为空" )
+    private Long userId;
+
+    @Schema(description = "新密码" )
+    @NotNull(message = "新密码不能为空" )
+    @Length(min = 32, max = 32, message = "新密码长度不符合要求" )
+    private String password;
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/dto/SysUserUpdatePasswordDto.java b/src/main/java/io/geekidea/boot/system/dto/SysUserUpdatePasswordDto.java
new file mode 100644
index 00000000..4df2b21e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/dto/SysUserUpdatePasswordDto.java
@@ -0,0 +1,34 @@
+package io.geekidea.boot.system.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * 修改系统用户密码
+ *
+ * @author geekidea
+ * @date 2023/6/24
+ **/
+@Data
+@Schema(description = "修改系统用户密码")
+public class SysUserUpdatePasswordDto {
+
+    @Schema(description = "旧密码")
+    @NotNull(message = "旧密码不能为空")
+    @Length(min = 32, max = 32, message = "旧密码长度不符合要求")
+    private String oldPassword;
+
+    @Schema(description = "新密码")
+    @NotNull(message = "新密码不能为空")
+    @Length(min = 32, max = 32, message = "新密码长度不符合要求")
+    private String password;
+
+    @Schema(description = "确认新密码")
+    @NotNull(message = "确认新密码不能为空")
+    @Length(min = 32, max = 32, message = "确认新密码长度不符合要求")
+    private String confirmPassword;
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/dto/SysUserUpdateProfileDto.java b/src/main/java/io/geekidea/boot/system/dto/SysUserUpdateProfileDto.java
new file mode 100644
index 00000000..3347e03b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/dto/SysUserUpdateProfileDto.java
@@ -0,0 +1,40 @@
+package io.geekidea.boot.system.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 修改系统用户个人信息参数
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@Schema(description = "修改系统用户个人信息参数" )
+public class SysUserUpdateProfileDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "昵称" )
+    @NotBlank(message = "昵称不能为空" )
+    @Length(max = 20, message = "昵称长度超过限制" )
+    private String nickname;
+
+    @Schema(description = "手机号码" )
+    @Length(max = 11, message = "手机号码长度超过限制" )
+    private String phone;
+
+    @Schema(description = "电子邮件" )
+    @Length(max = 255, message = "电子邮件长度超过限制" )
+    private String email;
+
+    @Schema(description = "头像")
+    private String head;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/system/entity/SysConfig.java b/src/main/java/io/geekidea/boot/system/entity/SysConfig.java
new file mode 100644
index 00000000..308d6a1e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/entity/SysConfig.java
@@ -0,0 +1,62 @@
+package io.geekidea.boot.system.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+
+import java.io.Serializable;
+import java.util.Date;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 系统配置
+ *
+ * @author geekidea
+ * @since 2023-11-27
+ */
+@Data
+@TableName("sys_config")
+@Schema(description = "系统配置")
+public class SysConfig implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "配置名称")
+    private String configName;
+
+    @Schema(description = "配置key")
+    private String configKey;
+
+    @Schema(description = "配置值")
+    private String configValue;
+
+    @Schema(description = "是否是系统字典类型")
+    private Boolean isSystem;
+
+    @Schema(description = "状态 1：正常，0：禁用")
+    private Boolean status;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/entity/SysDict.java b/src/main/java/io/geekidea/boot/system/entity/SysDict.java
new file mode 100644
index 00000000..7f20143e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/entity/SysDict.java
@@ -0,0 +1,62 @@
+package io.geekidea.boot.system.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+
+import java.io.Serializable;
+import java.util.Date;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 字典数据
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@TableName("sys_dict")
+@Schema(description = "字典数据")
+public class SysDict implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "字典值")
+    private String value;
+
+    @Schema(description = "字典名称")
+    private String label;
+
+    @Schema(description = "字典类型code")
+    private String dictCode;
+
+    @Schema(description = "状态 1：启用，0：禁用")
+    private Boolean status;
+
+    @Schema(description = "排序值")
+    private Integer sort;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/entity/SysDictType.java b/src/main/java/io/geekidea/boot/system/entity/SysDictType.java
new file mode 100644
index 00000000..5695fe35
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/entity/SysDictType.java
@@ -0,0 +1,56 @@
+package io.geekidea.boot.system.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+
+import java.io.Serializable;
+import java.util.Date;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 字典类型
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@TableName("sys_dict_type")
+@Schema(description = "字典类型")
+public class SysDictType implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "字典类型编码")
+    private String code;
+
+    @Schema(description = "字典类型名称")
+    private String name;
+
+    @Schema(description = "是否是系统字典类型")
+    private Boolean isSystem;
+
+    @Schema(description = "会员等级")
+    private String remark;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/entity/SysFile.java b/src/main/java/io/geekidea/boot/system/entity/SysFile.java
new file mode 100644
index 00000000..f49f30fc
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/entity/SysFile.java
@@ -0,0 +1,96 @@
+package io.geekidea.boot.system.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 系统文件
+ *
+ * @author geekidea
+ * @since 2023-11-26
+ */
+@Data
+@TableName("sys_file")
+@Schema(description = "系统文件")
+public class SysFile implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "日志链路ID")
+    private String traceId;
+
+    @Schema(description = "服务类型 1：本地服务，2：阿里云OSS")
+    private Integer serverType;
+
+    @Schema(description = "上传类型")
+    private String uploadType;
+
+    @Schema(description = "目录名称")
+    private String dirName;
+
+    @Schema(description = "源文件名称")
+    private String originalFileName;
+
+    @Schema(description = "生成的文件名称")
+    private String fileName;
+
+    @Schema(description = "文件内容类型")
+    private String contentType;
+
+    @Schema(description = "文件后缀")
+    private String extension;
+
+    @Schema(description = "文件大小")
+    private Long size;
+
+    @Schema(description = "文件大小MB")
+    private BigDecimal sizeMb;
+
+    @Schema(description = "访问的URL")
+    private String url;
+
+    @Schema(description = "系统类型 1：管理端，2：移动端")
+    private Integer systemType;
+
+    @Schema(description = "用户ID")
+    private Long userId;
+
+    @Schema(description = "本地文件服务时的物流文件位置")
+    private String filePath;
+
+    @Schema(description = "文件类型 1：图片，2：音视频，3：文档，4：文件")
+    private Integer fileType;
+
+    @Schema(description = "IP地址")
+    private String ip;
+
+    @Schema(description = "IP区域")
+    private String ipArea;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/entity/SysLog.java b/src/main/java/io/geekidea/boot/system/entity/SysLog.java
new file mode 100644
index 00000000..1062c220
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/entity/SysLog.java
@@ -0,0 +1,161 @@
+package io.geekidea.boot.system.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 系统日志
+ *
+ * @author geekidea
+ * @since 2023-02-16
+ */
+@Data
+@TableName("sys_log")
+@Schema(description = "系统日志")
+public class SysLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "日志链路ID")
+    private String traceId;
+
+    @Schema(description = "请求时间")
+    private String requestTime;
+
+    @Schema(description = "请求路径")
+    private String requestUrl;
+
+    @Schema(description = "权限编码")
+    private String permissionCode;
+
+    @Schema(description = "日志名称")
+    private String logName;
+
+    @Schema(description = "请求方式，GET/POST")
+    private String requestMethod;
+
+    @Schema(description = "内容类型")
+    private String contentType;
+
+    @Schema(description = "是否是JSON请求映射参数")
+    private Boolean isRequestBody;
+
+    @Schema(description = "token")
+    private String token;
+
+    @Schema(description = "模块名称")
+    private String moduleName;
+
+    @Schema(description = "controller类名称")
+    private String className;
+
+    @Schema(description = "controller方法名称")
+    private String methodName;
+
+    @Schema(description = "请求参数")
+    private String requestParam;
+
+    @Schema(description = "系统类型 1：Admin管理后台，2：APP移动端端")
+    private Integer systemType;
+
+    @Schema(description = "用户ID")
+    private Long userId;
+
+    @Schema(description = "请求ip")
+    private String requestIp;
+
+    @Schema(description = "IP国家")
+    private String ipCountry;
+
+    @Schema(description = "IP省份")
+    private String ipProvince;
+
+    @Schema(description = "IP城市")
+    private String ipCity;
+
+    @Schema(description = "IP区域描述")
+    private String ipAreaDesc;
+
+    @Schema(description = "IP运营商")
+    private String ipIsp;
+
+    @Schema(description = "0:访问日志,1:新增,2:修改,3:删除,4:详情,5:所有列表,6:分页列表,7:其它查询,8:上传文件,9:登录,10:退出")
+    private Integer logType;
+
+    @Schema(description = "响应时间")
+    private String responseTime;
+
+    @Schema(description = "0:失败,1:成功")
+    private Boolean responseSuccess;
+
+    @Schema(description = "响应结果状态码")
+    private Integer responseCode;
+
+    @Schema(description = "响应结果消息")
+    private String responseMessage;
+
+    @Schema(description = "响应数据")
+    private String responseData;
+
+    @Schema(description = "异常类名称")
+    private String exceptionName;
+
+    @Schema(description = "异常信息")
+    private String exceptionMessage;
+
+    @Schema(description = "耗时，单位：毫秒")
+    private Long diffTime;
+
+    @Schema(description = "耗时描述")
+    private String diffTimeDesc;
+
+    @Schema(description = "请求来源地址")
+    private String referer;
+
+    @Schema(description = "请求来源服务名")
+    private String origin;
+
+    @Schema(description = "请求来源类型")
+    private String sourceType;
+
+    @Schema(description = "是否手机 0：否，1：是")
+    private Boolean isMobile;
+
+    @Schema(description = "平台名称")
+    private String platformName;
+
+    @Schema(description = "浏览器名称")
+    private String browserName;
+
+    @Schema(description = "用户环境")
+    private String userAgent;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/entity/SysMenu.java b/src/main/java/io/geekidea/boot/system/entity/SysMenu.java
new file mode 100644
index 00000000..48bdec19
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/entity/SysMenu.java
@@ -0,0 +1,80 @@
+package io.geekidea.boot.system.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 系统菜单
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@TableName("sys_menu")
+@Schema(description = "系统菜单")
+public class SysMenu implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "菜单名称")
+    private String name;
+
+    @Schema(description = "父id")
+    private Long parentId;
+
+    @Schema(description = "菜单类型，1：目录，2：菜单，3：权限")
+    private Integer type;
+
+    @Schema(description = "菜单编码")
+    private String code;
+
+    @Schema(description = "前端路由地址")
+    private String routeUrl;
+
+    @Schema(description = "重定向")
+    private String routeRedirect;
+
+    @Schema(description = "组件路径")
+    private String componentPath;
+
+    @Schema(description = "菜单图标")
+    private String icon;
+
+    @Schema(description = "排序")
+    private Integer sort;
+
+    @Schema(description = "状态，0：禁用，1：启用")
+    private Boolean status;
+
+    @Schema(description = "是否显示,0：不显示，1：显示")
+    private Boolean isShow;
+
+    @Schema(description = "是否缓存，0：否 1：是")
+    private Boolean isCache;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/entity/SysRole.java b/src/main/java/io/geekidea/boot/system/entity/SysRole.java
new file mode 100644
index 00000000..d9ef72eb
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/entity/SysRole.java
@@ -0,0 +1,56 @@
+package io.geekidea.boot.system.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 系统角色
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@TableName("sys_role")
+@Schema(description = "系统角色")
+public class SysRole implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "角色名称")
+    private String name;
+
+    @Schema(description = "角色唯一编码")
+    private String code;
+
+    @Schema(description = "是否系统内置角色 1：是，0：否")
+    private Boolean isSystem;
+
+    @Schema(description = "角色备注")
+    private String remark;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/entity/SysRoleMenu.java b/src/main/java/io/geekidea/boot/system/entity/SysRoleMenu.java
new file mode 100644
index 00000000..65d4b3a2
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/entity/SysRoleMenu.java
@@ -0,0 +1,53 @@
+package io.geekidea.boot.system.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 角色菜单关系表
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@TableName("sys_role_menu")
+@Schema(description = "角色菜单关系表")
+public class SysRoleMenu implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "角色id")
+    private Long roleId;
+
+    @Schema(description = "菜单id")
+    private Long menuId;
+
+    @Schema(description = "是否用户选中 0：否，1：是")
+    private boolean isChoice;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/entity/SysUser.java b/src/main/java/io/geekidea/boot/system/entity/SysUser.java
new file mode 100644
index 00000000..f83bbbf4
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/entity/SysUser.java
@@ -0,0 +1,74 @@
+package io.geekidea.boot.system.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 系统用户
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@TableName("sys_user")
+@Schema(description = "系统用户")
+public class SysUser implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "用户名")
+    private String username;
+
+    @Schema(description = "昵称")
+    private String nickname;
+
+    @Schema(description = "密码")
+    private String password;
+
+    @Schema(description = "盐值")
+    private String salt;
+
+    @Schema(description = "手机号码")
+    private String phone;
+
+    @Schema(description = "电子邮件")
+    private String email;
+
+    @Schema(description = "头像")
+    private String head;
+
+    @Schema(description = "状态，0：禁用，1：启用")
+    private Boolean status;
+
+    @Schema(description = "角色id")
+    private Long roleId;
+
+    @Schema(description = "是否是超管 0：否，1：是")
+    private Boolean isAdmin;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/enums/SysMenuType.java b/src/main/java/io/geekidea/boot/system/enums/SysMenuType.java
new file mode 100644
index 00000000..99adb4e9
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/enums/SysMenuType.java
@@ -0,0 +1,65 @@
+package io.geekidea.boot.system.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 系统菜单类型
+ *
+ * @author geekidea
+ * @date 2024/1/21
+ **/
+public enum SysMenuType {
+
+    DIR(1, "目录"),
+    MENU(2, "菜单"),
+    PERMISSION(3, "权限");
+
+    private Integer code;
+    private String desc;
+
+    SysMenuType(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    private static final Map<Integer, SysMenuType> map = new HashMap<>();
+
+    static {
+        for (SysMenuType type : values()) {
+            map.put(type.code, type);
+        }
+    }
+
+    public static Integer getCode(SysMenuType type) {
+        if (type == null) {
+            return null;
+        }
+        return type.code;
+    }
+
+    public static SysMenuType get(Integer code) {
+        SysMenuType type = map.get(code);
+        if (type == null) {
+            return null;
+        }
+        return type;
+    }
+
+    public static String getDesc(Integer code) {
+        SysMenuType type = get(code);
+        if (type == null) {
+            return null;
+        }
+        return type.getDesc();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/listener/SysUserImportListener.java b/src/main/java/io/geekidea/boot/system/listener/SysUserImportListener.java
new file mode 100644
index 00000000..e2263536
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/listener/SysUserImportListener.java
@@ -0,0 +1,38 @@
+package io.geekidea.boot.system.listener;
+
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.read.listener.ReadListener;
+import com.alibaba.excel.util.ListUtils;
+import io.geekidea.boot.system.dto.SysUserExcelDto;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2023/12/25
+ **/
+@Slf4j
+public class SysUserImportListener implements ReadListener<SysUserExcelDto> {
+
+    /**
+     * 每隔10条存储数据库，实际使用中可以100条，然后清理list ，方便内存回收
+     */
+    private static final int BATCH_COUNT = 10;
+    /**
+     * 缓存的数据
+     */
+    private List<SysUserExcelDto> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
+
+
+    @Override
+    public void invoke(SysUserExcelDto data, AnalysisContext context) {
+
+    }
+
+    @Override
+    public void doAfterAllAnalysed(AnalysisContext context) {
+
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/mapper/SysConfigMapper.java b/src/main/java/io/geekidea/boot/system/mapper/SysConfigMapper.java
new file mode 100644
index 00000000..a25e4300
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/mapper/SysConfigMapper.java
@@ -0,0 +1,36 @@
+package io.geekidea.boot.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.system.entity.SysConfig;
+import io.geekidea.boot.system.query.SysConfigQuery;
+import io.geekidea.boot.system.vo.SysConfigVo;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 系统配置 Mapper 接口
+ *
+ * @author geekidea
+ * @since 2023-11-27
+ */
+@Mapper
+public interface SysConfigMapper extends BaseMapper<SysConfig> {
+
+    /**
+     * 系统配置详情
+     *
+     * @param id
+     * @return
+     */
+    SysConfigVo getSysConfigById(Long id);
+
+    /**
+     * 系统配置分页列表
+     *
+     * @param query
+     * @return
+     */
+    List<SysConfigVo> getSysConfigPage(SysConfigQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/mapper/SysDictMapper.java b/src/main/java/io/geekidea/boot/system/mapper/SysDictMapper.java
new file mode 100644
index 00000000..e2dfb0bb
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/mapper/SysDictMapper.java
@@ -0,0 +1,46 @@
+package io.geekidea.boot.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.system.entity.SysDict;
+import io.geekidea.boot.system.query.SysDictQuery;
+import io.geekidea.boot.system.vo.SysDictVo;
+import io.geekidea.boot.system.query.SysDictAppQuery;
+import io.geekidea.boot.system.vo.AppSysDictVo;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 字典数据 Mapper 接口
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Mapper
+public interface SysDictMapper extends BaseMapper<SysDict> {
+
+    /**
+     * 字典数据详情
+     *
+     * @param id
+     * @return
+     */
+    SysDictVo getSysDictById(Long id);
+
+    /**
+     * 字典数据分页列表
+     *
+     * @param query
+     * @return
+     */
+    List<SysDictVo> getSysDictPage(SysDictQuery query);
+
+    /**
+     * App字典数据列表
+     *
+     * @param query
+     * @return
+     */
+    List<AppSysDictVo> getAppSysDictList(SysDictAppQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/mapper/SysDictTypeMapper.java b/src/main/java/io/geekidea/boot/system/mapper/SysDictTypeMapper.java
new file mode 100644
index 00000000..7192a906
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/mapper/SysDictTypeMapper.java
@@ -0,0 +1,36 @@
+package io.geekidea.boot.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.system.entity.SysDictType;
+import io.geekidea.boot.system.query.SysDictTypeQuery;
+import io.geekidea.boot.system.vo.SysDictTypeVo;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 字典类型 Mapper 接口
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Mapper
+public interface SysDictTypeMapper extends BaseMapper<SysDictType> {
+
+    /**
+     * 字典类型详情
+     *
+     * @param id
+     * @return
+     */
+    SysDictTypeVo getSysDictTypeById(Long id);
+
+    /**
+     * 字典类型列表
+     *
+     * @param query
+     * @return
+     */
+    List<SysDictTypeVo> getSysDictTypeList(SysDictTypeQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/mapper/SysFileMapper.java b/src/main/java/io/geekidea/boot/system/mapper/SysFileMapper.java
new file mode 100644
index 00000000..8a91d68e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/mapper/SysFileMapper.java
@@ -0,0 +1,36 @@
+package io.geekidea.boot.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.system.entity.SysFile;
+import io.geekidea.boot.system.query.SysFileQuery;
+import io.geekidea.boot.system.vo.SysFileVo;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 系统文件 Mapper 接口
+ *
+ * @author geekidea
+ * @since 2023-11-26
+ */
+@Mapper
+public interface SysFileMapper extends BaseMapper<SysFile> {
+
+    /**
+     * 系统文件详情
+     *
+     * @param id
+     * @return
+     */
+    SysFileVo getSysFileById(Long id);
+
+    /**
+     * 系统文件分页列表
+     *
+     * @param query
+     * @return
+     */
+    List<SysFileVo> getSysFilePage(SysFileQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/mapper/SysLogMapper.java b/src/main/java/io/geekidea/boot/system/mapper/SysLogMapper.java
new file mode 100644
index 00000000..3fd890a8
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/mapper/SysLogMapper.java
@@ -0,0 +1,36 @@
+package io.geekidea.boot.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.system.vo.SysLogVo;
+import io.geekidea.boot.system.entity.SysLog;
+import io.geekidea.boot.system.query.SysLogQuery;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 系统日志 Mapper 接口
+ *
+ * @author geekidea
+ * @since 2023-02-16
+ */
+@Mapper
+public interface SysLogMapper extends BaseMapper<SysLog> {
+
+    /**
+     * 系统日志详情
+     *
+     * @param id
+     * @return
+     */
+    SysLogVo getSysLogById(Long id);
+
+    /**
+     * 系统日志分页列表
+     *
+     * @param query
+     * @return
+     */
+    List<SysLogVo> getSysLogPage(SysLogQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/mapper/SysMenuMapper.java b/src/main/java/io/geekidea/boot/system/mapper/SysMenuMapper.java
new file mode 100644
index 00000000..7254da22
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/mapper/SysMenuMapper.java
@@ -0,0 +1,78 @@
+package io.geekidea.boot.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.system.entity.SysMenu;
+import io.geekidea.boot.system.query.SysMenuQuery;
+import io.geekidea.boot.system.vo.SysMenuTreeVo;
+import io.geekidea.boot.system.vo.SysMenuVo;
+import io.geekidea.boot.system.vo.SysNavMenuTreeVo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 系统菜单 Mapper 接口
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Mapper
+public interface SysMenuMapper extends BaseMapper<SysMenu> {
+
+    /**
+     * 系统菜单详情
+     *
+     * @param id
+     * @return
+     */
+    SysMenuVo getSysMenuById(Long id);
+
+    /**
+     * 根据用户ID获取权限编码集合
+     *
+     * @param userId
+     * @return
+     */
+    List<String> getPermissionCodesByUserId(Long userId);
+
+    /**
+     * 获取系统菜单树形列表
+     *
+     * @param query
+     * @return
+     */
+    List<SysMenuTreeVo> getSysMenuTreeList(SysMenuQuery query);
+
+    /**
+     * 管理员获取所有导航菜单树形列表
+     *
+     * @return
+     */
+    List<SysNavMenuTreeVo> getNavMenuTreeAllList();
+
+    /**
+     * 获取当前登录用户导航菜单树形列表
+     *
+     * @param userId
+     * @return
+     */
+    List<SysNavMenuTreeVo> getNavMenuTreeList(Long userId);
+
+    /**
+     * 获取角色权限ID集合
+     *
+     * @param roleId
+     * @return
+     */
+    List<Long> getMenuIdsByRoleId(Long roleId);
+
+    /**
+     * 根据父菜单ID获取所有的子菜单ID集合
+     *
+     * @param parentIds
+     * @return
+     */
+    List<Long> getChildrenMenuIds(@Param("parentIds") List<Long> parentIds);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/mapper/SysRoleMapper.java b/src/main/java/io/geekidea/boot/system/mapper/SysRoleMapper.java
new file mode 100644
index 00000000..76bb97c9
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/mapper/SysRoleMapper.java
@@ -0,0 +1,36 @@
+package io.geekidea.boot.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.system.vo.SysRoleVo;
+import io.geekidea.boot.system.entity.SysRole;
+import io.geekidea.boot.system.query.SysRoleQuery;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 系统角色 Mapper 接口
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Mapper
+public interface SysRoleMapper extends BaseMapper<SysRole> {
+
+    /**
+     * 系统角色详情
+     *
+     * @param id
+     * @return
+     */
+    SysRoleVo getSysRoleById(Long id);
+
+    /**
+     * 系统角色分页列表
+     *
+     * @param query
+     * @return
+     */
+    List<SysRoleVo> getSysRolePage(SysRoleQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/mapper/SysRoleMenuMapper.java b/src/main/java/io/geekidea/boot/system/mapper/SysRoleMenuMapper.java
new file mode 100644
index 00000000..b32ab42b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/mapper/SysRoleMenuMapper.java
@@ -0,0 +1,16 @@
+package io.geekidea.boot.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.system.entity.SysRoleMenu;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 角色菜单关系表 Mapper 接口
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Mapper
+public interface SysRoleMenuMapper extends BaseMapper<SysRoleMenu> {
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/mapper/SysUserMapper.java b/src/main/java/io/geekidea/boot/system/mapper/SysUserMapper.java
new file mode 100644
index 00000000..b561df84
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/mapper/SysUserMapper.java
@@ -0,0 +1,52 @@
+package io.geekidea.boot.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.system.entity.SysUser;
+import io.geekidea.boot.system.query.SysUserQuery;
+import io.geekidea.boot.system.vo.SysUserVo;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 系统用户 Mapper 接口
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Mapper
+public interface SysUserMapper extends BaseMapper<SysUser> {
+
+    /**
+     * 系统用户详情
+     *
+     * @param id
+     * @return
+     */
+    SysUserVo getSysUserById(Long id);
+
+    /**
+     * 系统用户分页列表
+     *
+     * @param query
+     * @return
+     */
+    List<SysUserVo> getSysUserPage(SysUserQuery query);
+
+    /**
+     * 根据用户名获取登录用户对象
+     *
+     * @param username
+     * @return
+     */
+    SysUser getSysUserByUsername(String username);
+
+    /**
+     * 根据角色ID获取用户数量
+     *
+     * @param roleId
+     * @return
+     */
+    Integer getCountByRoleId(Long roleId);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/query/SysConfigQuery.java b/src/main/java/io/geekidea/boot/system/query/SysConfigQuery.java
new file mode 100644
index 00000000..7bcebb1a
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/query/SysConfigQuery.java
@@ -0,0 +1,25 @@
+package io.geekidea.boot.system.query;
+
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 系统配置查询参数
+ *
+ * @author geekidea
+ * @since 2023-11-27
+ */
+@Data
+@Schema(description = "系统配置查询参数")
+public class SysConfigQuery extends BasePageQuery {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "是否是系统字典类型")
+    private Boolean isSystem;
+
+    @Schema(description = "状态 1：正常，0：禁用")
+    private Boolean status;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/query/SysDictAppQuery.java b/src/main/java/io/geekidea/boot/system/query/SysDictAppQuery.java
new file mode 100644
index 00000000..0d145621
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/query/SysDictAppQuery.java
@@ -0,0 +1,21 @@
+package io.geekidea.boot.system.query;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * App字典数据查询参数
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "App字典数据查询参数")
+public class SysDictAppQuery {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "字典类型编码")
+    private String dictCode;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/query/SysDictQuery.java b/src/main/java/io/geekidea/boot/system/query/SysDictQuery.java
new file mode 100644
index 00000000..96caf30b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/query/SysDictQuery.java
@@ -0,0 +1,28 @@
+package io.geekidea.boot.system.query;
+
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * 字典数据查询参数
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "字典数据查询参数")
+public class SysDictQuery extends BasePageQuery {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "字典类型code")
+    @NotBlank(message = "字典类型不能为空")
+    private String dictCode;
+
+    @Schema(description = "状态 1：启用，0：禁用")
+    private Boolean status;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/query/SysDictTypeQuery.java b/src/main/java/io/geekidea/boot/system/query/SysDictTypeQuery.java
new file mode 100644
index 00000000..a3257226
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/query/SysDictTypeQuery.java
@@ -0,0 +1,19 @@
+package io.geekidea.boot.system.query;
+
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 字典类型查询参数
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "字典类型查询参数")
+public class SysDictTypeQuery extends BasePageQuery {
+    private static final long serialVersionUID = 1L;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/query/SysFileQuery.java b/src/main/java/io/geekidea/boot/system/query/SysFileQuery.java
new file mode 100644
index 00000000..99635495
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/query/SysFileQuery.java
@@ -0,0 +1,30 @@
+package io.geekidea.boot.system.query;
+
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 系统文件查询参数
+ *
+ * @author geekidea
+ * @since 2023-11-26
+ */
+@Data
+@Schema(description = "系统文件查询参数")
+public class SysFileQuery extends BasePageQuery {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "文件类型 1：图片，2：音视频，3：文档，4：文件")
+    private Integer fileType;
+
+    @Schema(description = "创建开始时间")
+    private Date createStartTime;
+
+    @Schema(description = "创建结束时间")
+    private Date createEndTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/query/SysLogQuery.java b/src/main/java/io/geekidea/boot/system/query/SysLogQuery.java
new file mode 100644
index 00000000..7d09f597
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/query/SysLogQuery.java
@@ -0,0 +1,48 @@
+package io.geekidea.boot.system.query;
+
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 系统日志查询参数
+ *
+ * @author geekidea
+ * @since 2023-02-16
+ */
+@Data
+@Schema(description = "系统日志查询参数")
+public class SysLogQuery extends BasePageQuery {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "模块名称")
+    private String moduleName;
+
+    @Schema(description = "请求路径")
+    private String requestUrl;
+
+    @Schema(description = "日志名称")
+    private String logName;
+
+    @Schema(description = "操作人")
+    private String username;
+
+    @Schema(description = "日志类型 0:访问日志,1:新增,2:修改,3:删除,4:详情,5:所有列表,6:分页列表,7:其它查询,8:上传文件,9:登录,10:退出")
+    private Integer logType;
+
+    @Schema(description = "响应状态 false:失败,true:成功")
+    private Boolean responseSuccess;
+
+    @Schema(description = "请求IP")
+    private String requestIp;
+
+    @Schema(description = "创建开始时间")
+    private Date createStartTime;
+
+    @Schema(description = "创建结束时间")
+    private Date createEndTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/query/SysMenuQuery.java b/src/main/java/io/geekidea/boot/system/query/SysMenuQuery.java
new file mode 100644
index 00000000..22c8e86e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/query/SysMenuQuery.java
@@ -0,0 +1,31 @@
+package io.geekidea.boot.system.query;
+
+import io.geekidea.boot.framework.query.DataRangeQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 系统菜单查询参数
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@Schema(description = "系统菜单查询参数")
+public class SysMenuQuery extends DataRangeQuery {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "菜单类型，1：目录，2：菜单，3：权限")
+    private Integer type;
+
+    @Schema(description = "状态，0：禁用，1：启用")
+    private Boolean status;
+
+    @Schema(description = "是否显示,0：不显示，1：显示")
+    private Boolean isShow;
+
+    @Schema(description = "关键字搜索")
+    private String keyword;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/query/SysRoleQuery.java b/src/main/java/io/geekidea/boot/system/query/SysRoleQuery.java
new file mode 100644
index 00000000..5fb4dec0
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/query/SysRoleQuery.java
@@ -0,0 +1,22 @@
+package io.geekidea.boot.system.query;
+
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 系统角色查询参数
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@Schema(description = "系统角色查询参数")
+public class SysRoleQuery extends BasePageQuery {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "是否系统内置角色 1：是，0：否")
+    private Boolean isSystem;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/query/SysUserQuery.java b/src/main/java/io/geekidea/boot/system/query/SysUserQuery.java
new file mode 100644
index 00000000..ca201c33
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/query/SysUserQuery.java
@@ -0,0 +1,42 @@
+package io.geekidea.boot.system.query;
+
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 系统用户查询参数
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@Schema(description = "系统用户查询参数")
+public class SysUserQuery extends BasePageQuery {
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "用户名")
+    private String username;
+
+    @Schema(description = "用户昵称")
+    private String nickname;
+
+    @Schema(description = "角色ID")
+    private Long roleId;
+
+    @Schema(description = "手机号码")
+    private String phone;
+
+    @Schema(description = "状态，0：禁用，1：启用")
+    private Boolean status;
+
+    @Schema(description = "创建开始时间")
+    private Date startCreateTime;
+
+    @Schema(description = "创建结束时间")
+    private Date endCreateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/service/SysConfigService.java b/src/main/java/io/geekidea/boot/system/service/SysConfigService.java
new file mode 100644
index 00000000..557b8878
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/SysConfigService.java
@@ -0,0 +1,73 @@
+package io.geekidea.boot.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.system.dto.SysConfigDto;
+import io.geekidea.boot.system.entity.SysConfig;
+import io.geekidea.boot.system.query.SysConfigQuery;
+import io.geekidea.boot.system.vo.SysConfigVo;
+
+
+/**
+ * 系统配置 服务接口
+ *
+ * @author geekidea
+ * @since 2023-11-27
+ */
+public interface SysConfigService extends IService<SysConfig> {
+
+    /**
+     * 添加系统配置
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean addSysConfig(SysConfigDto dto);
+
+    /**
+     * 修改系统配置
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean updateSysConfig(SysConfigDto dto);
+
+    /**
+     * 删除系统配置
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    boolean deleteSysConfig(Long id);
+
+    /**
+     * 系统配置详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    SysConfigVo getSysConfigById(Long id);
+
+    /**
+     * 系统配置分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    Paging<SysConfigVo> getSysConfigPage(SysConfigQuery query);
+
+    /**
+     * 检查configKey是否存在
+     *
+     * @param configKey
+     * @return
+     * @throws Exception
+     */
+    void checkConfigKeyExists(String configKey);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/SysDictService.java b/src/main/java/io/geekidea/boot/system/service/SysDictService.java
new file mode 100644
index 00000000..94f84788
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/SysDictService.java
@@ -0,0 +1,108 @@
+package io.geekidea.boot.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.system.dto.SysDictDto;
+import io.geekidea.boot.system.entity.SysDict;
+import io.geekidea.boot.system.query.SysDictAppQuery;
+import io.geekidea.boot.system.query.SysDictQuery;
+import io.geekidea.boot.system.vo.AppSysDictVo;
+import io.geekidea.boot.system.vo.SysDictVo;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 字典数据 服务接口
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+public interface SysDictService extends IService<SysDict> {
+
+    /**
+     * 添加字典数据
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean addSysDict(SysDictDto dto);
+
+    /**
+     * 修改字典数据
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean updateSysDict(SysDictDto dto);
+
+    /**
+     * 删除字典数据
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    boolean deleteSysDict(Long id);
+
+    /**
+     * 字典数据详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    SysDictVo getSysDictById(Long id);
+
+    /**
+     * 字典数据分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    Paging<SysDictVo> getSysDictPage(SysDictQuery query);
+
+    /**
+     * App字典数据列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    Map<String, List<AppSysDictVo>> getAppSysDictList(SysDictAppQuery query);
+
+    /**
+     * 根据字典编码获取字典列表
+     *
+     * @param dictCode
+     * @return
+     * @throws Exception
+     */
+    List<SysDict> getSysDictList(String dictCode);
+
+    /**
+     * 根据字典编码和label获取字典对象
+     *
+     * @param dictCode
+     * @param value
+     * @return
+     * @throws Exception
+     */
+    SysDict getSysDictByValue(String dictCode, Serializable value);
+
+    /**
+     * 根据字典编码和label获取字典值
+     *
+     * @param dictCode
+     * @param value
+     * @return
+     * @throws Exception
+     */
+    String getSysDictLabelByValue(String dictCode, Serializable value);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/SysDictTypeService.java b/src/main/java/io/geekidea/boot/system/service/SysDictTypeService.java
new file mode 100644
index 00000000..ec0b7eb3
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/SysDictTypeService.java
@@ -0,0 +1,74 @@
+package io.geekidea.boot.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.system.dto.SysDictTypeDto;
+import io.geekidea.boot.system.entity.SysDictType;
+import io.geekidea.boot.system.query.SysDictTypeQuery;
+import io.geekidea.boot.system.vo.SysDictTypeVo;
+
+import java.util.List;
+
+
+/**
+ * 字典类型 服务接口
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+public interface SysDictTypeService extends IService<SysDictType> {
+
+    /**
+     * 添加字典类型
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean addSysDictType(SysDictTypeDto dto);
+
+    /**
+     * 修改字典类型
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean updateSysDictType(SysDictTypeDto dto);
+
+    /**
+     * 删除字典类型
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    boolean deleteSysDictType(Long id);
+
+    /**
+     * 字典类型详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    SysDictTypeVo getSysDictTypeById(Long id);
+
+    /**
+     * 字典类型列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    List<SysDictTypeVo> getSysDictTypeList(SysDictTypeQuery query);
+
+    /**
+     * 检查code是否存在
+     *
+     * @param code
+     * @return
+     * @throws Exception
+     */
+    void checkCodeExists(String code);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/SysFileService.java b/src/main/java/io/geekidea/boot/system/service/SysFileService.java
new file mode 100644
index 00000000..279c5aae
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/SysFileService.java
@@ -0,0 +1,55 @@
+package io.geekidea.boot.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.system.dto.SysFileDto;
+import io.geekidea.boot.system.entity.SysFile;
+import io.geekidea.boot.system.query.SysFileQuery;
+import io.geekidea.boot.system.vo.SysFileVo;
+
+
+/**
+ * 系统文件 服务接口
+ *
+ * @author geekidea
+ * @since 2023-11-26
+ */
+public interface SysFileService extends IService<SysFile> {
+
+    /**
+     * 修改系统文件
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean updateSysFile(SysFileDto dto);
+
+    /**
+     * 删除系统文件
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    boolean deleteSysFile(Long id);
+
+    /**
+     * 系统文件详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    SysFileVo getSysFileById(Long id);
+
+    /**
+     * 系统文件分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    Paging<SysFileVo> getSysFilePage(SysFileQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/SysLogService.java b/src/main/java/io/geekidea/boot/system/service/SysLogService.java
new file mode 100644
index 00000000..0f24fa18
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/SysLogService.java
@@ -0,0 +1,35 @@
+package io.geekidea.boot.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.system.entity.SysLog;
+import io.geekidea.boot.system.query.SysLogQuery;
+import io.geekidea.boot.system.vo.SysLogVo;
+
+/**
+ * 系统日志 服务接口
+ *
+ * @author geekidea
+ * @since 2023-02-16
+ */
+public interface SysLogService extends IService<SysLog> {
+
+    /**
+     * 系统日志详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    SysLogVo getSysLogById(Long id);
+
+    /**
+     * 系统日志分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    Paging<SysLogVo> getSysLogPage(SysLogQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/SysMenuService.java b/src/main/java/io/geekidea/boot/system/service/SysMenuService.java
new file mode 100644
index 00000000..32daeec6
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/SysMenuService.java
@@ -0,0 +1,91 @@
+package io.geekidea.boot.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.system.dto.SysMenuDto;
+import io.geekidea.boot.system.entity.SysMenu;
+import io.geekidea.boot.system.query.SysMenuQuery;
+import io.geekidea.boot.system.vo.SysMenuTreeVo;
+import io.geekidea.boot.system.vo.SysMenuVo;
+import io.geekidea.boot.system.vo.SysNavMenuTreeVo;
+
+import java.util.List;
+
+/**
+ * 系统菜单 服务接口
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+public interface SysMenuService extends IService<SysMenu> {
+
+    /**
+     * 添加系统菜单
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean addSysMenu(SysMenuDto dto);
+
+    /**
+     * 修改系统菜单
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean updateSysMenu(SysMenuDto dto);
+
+    /**
+     * 删除系统菜单
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    boolean deleteSysMenu(Long id);
+
+    /**
+     * 系统菜单详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    SysMenuVo getSysMenuById(Long id);
+
+    /**
+     * 获取所有的系统菜单树形列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    List<SysMenuTreeVo> getAllSysMenuTreeList(SysMenuQuery query);
+
+    /**
+     * 获取启用的系统菜单树形列表
+     *
+     * @return
+     * @throws Exception
+     */
+    List<SysMenuTreeVo> getSysMenuTreeList();
+
+    /**
+     * 获取当前登录用户导航菜单树形列表
+     *
+     * @return
+     * @throws Exception
+     */
+    List<SysNavMenuTreeVo> getNavMenuTreeList();
+
+    /**
+     * 获取角色权限ID集合
+     *
+     * @param roleId
+     * @return
+     * @throws Exception
+     */
+    List<Long> getMenuIdsByRoleId(Long roleId);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/SysRoleMenuService.java b/src/main/java/io/geekidea/boot/system/service/SysRoleMenuService.java
new file mode 100644
index 00000000..65e1e66f
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/SysRoleMenuService.java
@@ -0,0 +1,30 @@
+package io.geekidea.boot.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.system.entity.SysRoleMenu;
+
+import java.util.List;
+
+/**
+ * 角色菜单关系表 服务接口
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+public interface SysRoleMenuService extends IService<SysRoleMenu> {
+
+    /**
+     * 根据角色ID删除角色权限关联关系
+     *
+     * @param roleId
+     */
+    void deleteSysRoleMenuByRoleId(Long roleId);
+
+    /**
+     * 根据菜单ID删除角色菜单关联关系
+     *
+     * @param menuIds
+     */
+    void deleteSysRoleMenuByMenuIds(List<Long> menuIds);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/SysRoleService.java b/src/main/java/io/geekidea/boot/system/service/SysRoleService.java
new file mode 100644
index 00000000..1ed887f9
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/SysRoleService.java
@@ -0,0 +1,92 @@
+package io.geekidea.boot.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.system.dto.RoleMenusDto;
+import io.geekidea.boot.system.dto.SysRoleDto;
+import io.geekidea.boot.system.entity.SysRole;
+import io.geekidea.boot.system.query.SysRoleQuery;
+import io.geekidea.boot.system.vo.SysRoleVo;
+
+import java.util.List;
+
+/**
+ * 系统角色 服务接口
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+public interface SysRoleService extends IService<SysRole> {
+
+    /**
+     * 添加系统角色
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean addSysRole(SysRoleDto dto);
+
+    /**
+     * 修改系统角色
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean updateSysRole(SysRoleDto dto);
+
+    /**
+     * 删除系统角色
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    boolean deleteSysRole(Long id);
+
+    /**
+     * 系统角色详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    SysRoleVo getSysRoleById(Long id);
+
+    /**
+     * 系统角色分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    Paging<SysRoleVo> getSysRolePage(SysRoleQuery query);
+
+    /**
+     * 获取所有角色列表
+     *
+     * @return
+     * @throws Exception
+     */
+    List<SysRole> getSysRoleAllList();
+
+    /**
+     * 设置角色权限
+     *
+     * @param roleMenusDto
+     * @return
+     * @throws Exception
+     */
+    boolean setRoleMenus(RoleMenusDto roleMenusDto);
+
+    /**
+     * 检查code是否存在
+     *
+     * @param code
+     * @return
+     * @throws Exception
+     */
+    void checkCodeExists(String code);
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/SysUserService.java b/src/main/java/io/geekidea/boot/system/service/SysUserService.java
new file mode 100644
index 00000000..0528f06c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/SysUserService.java
@@ -0,0 +1,112 @@
+package io.geekidea.boot.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.system.dto.SysUserDto;
+import io.geekidea.boot.system.dto.SysUserResetPasswordDto;
+import io.geekidea.boot.system.dto.SysUserUpdatePasswordDto;
+import io.geekidea.boot.system.dto.SysUserUpdateProfileDto;
+import io.geekidea.boot.system.entity.SysUser;
+import io.geekidea.boot.system.query.SysUserQuery;
+import io.geekidea.boot.system.vo.SysUserVo;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * 系统用户 服务接口
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+public interface SysUserService extends IService<SysUser> {
+
+    /**
+     * 添加系统用户
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean addSysUser(SysUserDto dto);
+
+    /**
+     * 修改系统用户
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean updateSysUser(SysUserDto dto);
+
+    /**
+     * 删除系统用户
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    boolean deleteSysUser(Long id);
+
+    /**
+     * 系统用户详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    SysUserVo getSysUserById(Long id);
+
+    /**
+     * 系统用户分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    Paging<SysUserVo> getSysUserPage(SysUserQuery query);
+
+    /**
+     * 重置系统用户密码
+     *
+     * @param sysUserResetPasswordDto
+     * @return
+     * @throws Exception
+     */
+    boolean resetSysUserPassword(SysUserResetPasswordDto sysUserResetPasswordDto);
+
+    /**
+     * 修改个人信息
+     *
+     * @param sysUserUpdateProfileDto
+     * @return
+     * @throws Exception
+     */
+    boolean updateProfile(SysUserUpdateProfileDto sysUserUpdateProfileDto);
+
+    /**
+     * 修改系统用户密码
+     *
+     * @param sysUserUpdatePasswordDto
+     * @return
+     * @throws Exception
+     */
+    boolean updatePassword(SysUserUpdatePasswordDto sysUserUpdatePasswordDto);
+
+    /**
+     * 检查username是否存在
+     *
+     * @param username
+     * @return
+     * @throws Exception
+     */
+    void checkUsernameExists(String username);
+
+    /**
+     * 导入Excel用户数据
+     *
+     * @param multipartFile
+     * @return
+     * @throws Exception
+     */
+    boolean importExcel(MultipartFile multipartFile) throws Exception;
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/impl/SysConfigServiceImpl.java b/src/main/java/io/geekidea/boot/system/service/impl/SysConfigServiceImpl.java
new file mode 100644
index 00000000..5eb81c69
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/impl/SysConfigServiceImpl.java
@@ -0,0 +1,89 @@
+package io.geekidea.boot.system.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.framework.page.OrderByItem;
+import io.geekidea.boot.framework.page.OrderMapping;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.system.dto.SysConfigDto;
+import io.geekidea.boot.system.entity.SysConfig;
+import io.geekidea.boot.system.mapper.SysConfigMapper;
+import io.geekidea.boot.system.query.SysConfigQuery;
+import io.geekidea.boot.system.service.SysConfigService;
+import io.geekidea.boot.system.vo.SysConfigVo;
+import io.geekidea.boot.util.PagingUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 系统配置 服务实现类
+ *
+ * @author geekidea
+ * @since 2023-11-27
+ */
+@Slf4j
+@Service
+public class SysConfigServiceImpl extends ServiceImpl<SysConfigMapper, SysConfig> implements SysConfigService {
+
+    @Autowired
+    private SysConfigMapper sysConfigMapper;
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean addSysConfig(SysConfigDto dto) {
+        checkConfigKeyExists(dto.getConfigKey());
+        SysConfig sysConfig = new SysConfig();
+        BeanUtils.copyProperties(dto, sysConfig);
+        return save(sysConfig);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updateSysConfig(SysConfigDto dto) {
+        Long id = dto.getId();
+        SysConfig sysConfig = getById(id);
+        if (sysConfig == null) {
+            throw new BusinessException("系统配置不存在");
+        }
+        BeanUtils.copyProperties(dto, sysConfig);
+        return updateById(sysConfig);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean deleteSysConfig(Long id) {
+        return removeById(id);
+    }
+
+    @Override
+    public SysConfigVo getSysConfigById(Long id) {
+        return sysConfigMapper.getSysConfigById(id);
+    }
+
+    @Override
+    public Paging<SysConfigVo> getSysConfigPage(SysConfigQuery query) {
+        OrderMapping orderMapping = new OrderMapping();
+        orderMapping.put("createTime", "create_time");
+        PagingUtil.handlePage(query, orderMapping, OrderByItem.desc("id"));
+        List<SysConfigVo> list = sysConfigMapper.getSysConfigPage(query);
+        Paging<SysConfigVo> paging = new Paging<>(list);
+        return paging;
+    }
+
+    @Override
+    public void checkConfigKeyExists(String configKey) {
+        LambdaQueryWrapper<SysConfig> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SysConfig::getConfigKey, configKey);
+        long count = count(wrapper);
+        if (count > 0) {
+            throw new BusinessException(configKey + "配置key已经存在");
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/impl/SysDictServiceImpl.java b/src/main/java/io/geekidea/boot/system/service/impl/SysDictServiceImpl.java
new file mode 100644
index 00000000..26db5456
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/impl/SysDictServiceImpl.java
@@ -0,0 +1,118 @@
+package io.geekidea.boot.system.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.framework.page.OrderByItem;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.system.dto.SysDictDto;
+import io.geekidea.boot.system.entity.SysDict;
+import io.geekidea.boot.system.mapper.SysDictMapper;
+import io.geekidea.boot.system.query.SysDictAppQuery;
+import io.geekidea.boot.system.query.SysDictQuery;
+import io.geekidea.boot.system.service.SysDictService;
+import io.geekidea.boot.system.vo.AppSysDictVo;
+import io.geekidea.boot.system.vo.SysDictVo;
+import io.geekidea.boot.util.PagingUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 字典数据 服务实现类
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Slf4j
+@Service
+public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> implements SysDictService {
+
+    @Autowired
+    private SysDictMapper sysDictMapper;
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean addSysDict(SysDictDto dto) {
+        SysDict sysDict = new SysDict();
+        BeanUtils.copyProperties(dto, sysDict);
+        return save(sysDict);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updateSysDict(SysDictDto dto) {
+        Long id = dto.getId();
+        SysDict sysDict = getById(id);
+        if (sysDict == null) {
+            throw new BusinessException("字典数据不存在");
+        }
+        BeanUtils.copyProperties(dto, sysDict);
+        return updateById(sysDict);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean deleteSysDict(Long id) {
+        return removeById(id);
+    }
+
+    @Override
+    public SysDictVo getSysDictById(Long id) {
+        return sysDictMapper.getSysDictById(id);
+    }
+
+    @Override
+    public Paging<SysDictVo> getSysDictPage(SysDictQuery query) {
+        PagingUtil.handlePage(query, OrderByItem.orderBy("status desc,sort,id"));
+        List<SysDictVo> list = sysDictMapper.getSysDictPage(query);
+        Paging<SysDictVo> paging = new Paging<>(list);
+        return paging;
+    }
+
+    @Override
+    public Map<String, List<AppSysDictVo>> getAppSysDictList(SysDictAppQuery query) {
+        List<AppSysDictVo> list = sysDictMapper.getAppSysDictList(query);
+        if (CollectionUtils.isNotEmpty(list)) {
+            Map<String, List<AppSysDictVo>> map = list.stream().collect(Collectors.groupingBy(AppSysDictVo::getDictCode));
+            return map;
+        }
+        return null;
+    }
+
+    @Override
+    public List<SysDict> getSysDictList(String dictCode) {
+        LambdaQueryWrapper<SysDict> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SysDict::getDictCode, dictCode);
+        wrapper.eq(SysDict::getStatus, true);
+        wrapper.orderByAsc(SysDict::getSort);
+        return list(wrapper);
+    }
+
+    @Override
+    public SysDict getSysDictByValue(String dictCode, Serializable value) {
+        LambdaQueryWrapper<SysDict> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SysDict::getDictCode, dictCode);
+        wrapper.eq(SysDict::getValue, value);
+        wrapper.eq(SysDict::getStatus, true);
+        return getOne(wrapper);
+    }
+
+    @Override
+    public String getSysDictLabelByValue(String dictCode, Serializable value) {
+        SysDict sysDict = getSysDictByValue(dictCode, value);
+        if (sysDict != null) {
+            return sysDict.getLabel();
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/impl/SysDictTypeServiceImpl.java b/src/main/java/io/geekidea/boot/system/service/impl/SysDictTypeServiceImpl.java
new file mode 100644
index 00000000..ba77baeb
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/impl/SysDictTypeServiceImpl.java
@@ -0,0 +1,91 @@
+package io.geekidea.boot.system.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.system.dto.SysDictTypeDto;
+import io.geekidea.boot.system.entity.SysDictType;
+import io.geekidea.boot.system.mapper.SysDictTypeMapper;
+import io.geekidea.boot.system.query.SysDictTypeQuery;
+import io.geekidea.boot.system.service.SysDictTypeService;
+import io.geekidea.boot.system.vo.SysDictTypeVo;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 字典类型 服务实现类
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Slf4j
+@Service
+public class SysDictTypeServiceImpl extends ServiceImpl<SysDictTypeMapper, SysDictType> implements SysDictTypeService {
+
+    @Autowired
+    private SysDictTypeMapper sysDictTypeMapper;
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean addSysDictType(SysDictTypeDto dto) {
+        checkCodeExists(dto.getCode());
+        SysDictType sysDictType = new SysDictType();
+        BeanUtils.copyProperties(dto, sysDictType);
+        return save(sysDictType);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updateSysDictType(SysDictTypeDto dto) {
+        Long id = dto.getId();
+        SysDictType sysDictType = getById(id);
+        if (sysDictType == null) {
+            throw new BusinessException("字典类型不存在");
+        }
+        sysDictType.setIsSystem(dto.getIsSystem());
+        sysDictType.setName(dto.getName());
+        sysDictType.setRemark(dto.getRemark());
+        return updateById(sysDictType);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean deleteSysDictType(Long id) {
+        SysDictType sysDictType = getById(id);
+        if (sysDictType == null) {
+            throw new BusinessException("系统字典类型不存在");
+        }
+        Boolean isSystem = sysDictType.getIsSystem();
+        if (isSystem) {
+            throw new BusinessException("系统类型不能删除");
+        }
+        return removeById(id);
+    }
+
+    @Override
+    public SysDictTypeVo getSysDictTypeById(Long id) {
+        return sysDictTypeMapper.getSysDictTypeById(id);
+    }
+
+    @Override
+    public List<SysDictTypeVo> getSysDictTypeList(SysDictTypeQuery query) {
+        List<SysDictTypeVo> list = sysDictTypeMapper.getSysDictTypeList(query);
+        return list;
+    }
+
+    @Override
+    public void checkCodeExists(String code) {
+        LambdaQueryWrapper<SysDictType> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SysDictType::getCode, code);
+        long count = count(wrapper);
+        if (count > 0) {
+            throw new BusinessException(code + "类型编码已经存在");
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/impl/SysFileServiceImpl.java b/src/main/java/io/geekidea/boot/system/service/impl/SysFileServiceImpl.java
new file mode 100644
index 00000000..1d60b92d
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/impl/SysFileServiceImpl.java
@@ -0,0 +1,69 @@
+package io.geekidea.boot.system.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.framework.page.OrderByItem;
+import io.geekidea.boot.framework.page.OrderMapping;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.system.dto.SysFileDto;
+import io.geekidea.boot.system.entity.SysFile;
+import io.geekidea.boot.system.mapper.SysFileMapper;
+import io.geekidea.boot.system.query.SysFileQuery;
+import io.geekidea.boot.system.service.SysFileService;
+import io.geekidea.boot.system.vo.SysFileVo;
+import io.geekidea.boot.util.PagingUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 系统文件 服务实现类
+ *
+ * @author geekidea
+ * @since 2023-11-26
+ */
+@Slf4j
+@Service
+public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements SysFileService {
+
+    @Autowired
+    private SysFileMapper sysFileMapper;
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updateSysFile(SysFileDto dto) {
+        Long id = dto.getId();
+        SysFile sysFile = getById(id);
+        if (sysFile == null) {
+            throw new BusinessException("系统文件不存在");
+        }
+        BeanUtils.copyProperties(dto, sysFile);
+        return updateById(sysFile);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean deleteSysFile(Long id) {
+        return removeById(id);
+    }
+
+    @Override
+    public SysFileVo getSysFileById(Long id) {
+        return sysFileMapper.getSysFileById(id);
+    }
+
+    @Override
+    public Paging<SysFileVo> getSysFilePage(SysFileQuery query) {
+        OrderMapping orderMapping = new OrderMapping();
+        orderMapping.put("createTime", "create_time");
+        PagingUtil.handlePage(query, orderMapping, OrderByItem.desc("id"));
+        List<SysFileVo> list = sysFileMapper.getSysFilePage(query);
+        Paging<SysFileVo> paging = new Paging<>(list);
+        return paging;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/impl/SysLogServiceImpl.java b/src/main/java/io/geekidea/boot/system/service/impl/SysLogServiceImpl.java
new file mode 100644
index 00000000..289bf923
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/impl/SysLogServiceImpl.java
@@ -0,0 +1,47 @@
+package io.geekidea.boot.system.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.framework.page.OrderByItem;
+import io.geekidea.boot.framework.page.OrderMapping;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.system.entity.SysLog;
+import io.geekidea.boot.system.mapper.SysLogMapper;
+import io.geekidea.boot.system.query.SysLogQuery;
+import io.geekidea.boot.system.service.SysLogService;
+import io.geekidea.boot.system.vo.SysLogVo;
+import io.geekidea.boot.util.PagingUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 系统日志 服务实现类
+ *
+ * @author geekidea
+ * @since 2023-02-16
+ */
+@Slf4j
+@Service
+public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> implements SysLogService {
+
+    @Autowired
+    private SysLogMapper sysLogMapper;
+
+    @Override
+    public SysLogVo getSysLogById(Long id) {
+        return sysLogMapper.getSysLogById(id);
+    }
+
+    @Override
+    public Paging<SysLogVo> getSysLogPage(SysLogQuery query) {
+        OrderMapping orderMapping = new OrderMapping();
+        orderMapping.put("createTime", "create_time");
+        PagingUtil.handlePage(query, orderMapping, OrderByItem.desc("id"));
+        List<SysLogVo> list = sysLogMapper.getSysLogPage(query);
+        Paging<SysLogVo> paging = new Paging<>(list);
+        return paging;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/impl/SysMenuServiceImpl.java b/src/main/java/io/geekidea/boot/system/service/impl/SysMenuServiceImpl.java
new file mode 100644
index 00000000..30f2711b
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/impl/SysMenuServiceImpl.java
@@ -0,0 +1,190 @@
+package io.geekidea.boot.system.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.auth.util.LoginUtil;
+import io.geekidea.boot.common.constant.SystemConstant;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.system.dto.SysMenuDto;
+import io.geekidea.boot.system.entity.SysMenu;
+import io.geekidea.boot.system.enums.SysMenuType;
+import io.geekidea.boot.system.mapper.SysMenuMapper;
+import io.geekidea.boot.system.query.SysMenuQuery;
+import io.geekidea.boot.system.service.SysMenuService;
+import io.geekidea.boot.system.service.SysRoleMenuService;
+import io.geekidea.boot.system.vo.SysMenuTreeVo;
+import io.geekidea.boot.system.vo.SysMenuVo;
+import io.geekidea.boot.system.vo.SysNavMenuTreeVo;
+import io.geekidea.boot.util.ObjectValueUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 系统菜单 服务实现类
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Slf4j
+@Service
+public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
+
+    @Autowired
+    private SysMenuMapper sysMenuMapper;
+
+    @Autowired
+    private SysRoleMenuService sysRoleMenuService;
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean addSysMenu(SysMenuDto dto) {
+        SysMenu sysMenu = new SysMenu();
+        BeanUtils.copyProperties(dto, sysMenu);
+        Long parentId = dto.getParentId();
+        if (parentId == null) {
+            sysMenu.setParentId(SystemConstant.ROOT_MENU_ID);
+        }
+        return save(sysMenu);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updateSysMenu(SysMenuDto dto) {
+        Long id = dto.getId();
+        SysMenu sysMenu = getById(id);
+        if (sysMenu == null) {
+            throw new BusinessException("系统菜单不存在");
+        }
+        BeanUtils.copyProperties(dto, sysMenu);
+        return updateById(sysMenu);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean deleteSysMenu(Long id) {
+        // 获取菜单
+        SysMenu sysMenu = getById(id);
+        if (sysMenu == null) {
+            throw new BusinessException("菜单数据不存在");
+        }
+        // 所有关联菜单ID集合
+        List<Long> deleteMenuIds = new ArrayList<>();
+        deleteMenuIds.add(id);
+        Integer type = sysMenu.getType();
+        SysMenuType sysMenuType = SysMenuType.get(type);
+        if (SysMenuType.DIR == sysMenuType) {
+            // 如果是目录，则先查询所有的子菜单ID，再根据子菜单ID查询所有的权限菜单ID
+            List<Long> menuIds = sysMenuMapper.getChildrenMenuIds(Arrays.asList(id));
+            deleteMenuIds.addAll(menuIds);
+            if (CollectionUtils.isNotEmpty(menuIds)) {
+                List<Long> permissionMenuIds = sysMenuMapper.getChildrenMenuIds(menuIds);
+                deleteMenuIds.addAll(permissionMenuIds);
+            }
+        } else if (SysMenuType.MENU == sysMenuType) {
+            // 如果是菜单，则查询所有的权限子菜单
+            List<Long> menuIds = sysMenuMapper.getChildrenMenuIds(Arrays.asList(id));
+            if (CollectionUtils.isNotEmpty(menuIds)) {
+                deleteMenuIds.addAll(menuIds);
+            }
+        }
+        // 删除角色菜单关系表中的相关菜单关联
+        sysRoleMenuService.deleteSysRoleMenuByMenuIds(deleteMenuIds);
+        // 删除所有关联菜单
+        return removeByIds(deleteMenuIds);
+    }
+
+    @Override
+    public SysMenuVo getSysMenuById(Long id) {
+        return sysMenuMapper.getSysMenuById(id);
+    }
+
+    @Override
+    public List<SysMenuTreeVo> getAllSysMenuTreeList(SysMenuQuery query) {
+        List<SysMenuTreeVo> list = sysMenuMapper.getSysMenuTreeList(query);
+        if (CollectionUtils.isEmpty(list)) {
+            return null;
+        }
+        // 如果搜索条件有值，则直接返回普通列表
+        boolean flag = ObjectValueUtil.isHaveValue(query);
+        if (flag) {
+            return list;
+        }
+        // 递归返回树形列表
+        return recursionSysMenuTreeList(0L, list);
+    }
+
+    @Override
+    public List<SysMenuTreeVo> getSysMenuTreeList() {
+        SysMenuQuery query = new SysMenuQuery();
+        query.setStatus(true);
+        List<SysMenuTreeVo> list = sysMenuMapper.getSysMenuTreeList(query);
+        if (CollectionUtils.isEmpty(list)) {
+            return null;
+        }
+        // 递归返回树形列表
+        return recursionSysMenuTreeList(0L, list);
+    }
+
+    @Override
+    public List<SysNavMenuTreeVo> getNavMenuTreeList() {
+        Long userId = LoginUtil.getUserId();
+        // 如果是管理员，则查询所有可用菜单，否则获取当前用户所有可用的菜单
+        boolean isAdmin = LoginUtil.isAdmin();
+        List<SysNavMenuTreeVo> list;
+        if (isAdmin) {
+            list = sysMenuMapper.getNavMenuTreeAllList();
+        } else {
+            list = sysMenuMapper.getNavMenuTreeList(userId);
+        }
+        // 递归返回树形列表
+        return recursionSysNavMenuTreeList(0L, list);
+    }
+
+    @Override
+    public List<Long> getMenuIdsByRoleId(Long roleId) {
+        return sysMenuMapper.getMenuIdsByRoleId(roleId);
+    }
+
+    /**
+     * 递归设置树形菜单
+     *
+     * @param parentId
+     * @param list
+     * @return
+     */
+    private List<SysMenuTreeVo> recursionSysMenuTreeList(Long parentId, List<SysMenuTreeVo> list) {
+        return list.stream()
+                .filter(vo -> vo.getParentId().equals(parentId))
+                .map(item -> {
+                    item.setChildren(recursionSysMenuTreeList(item.getId(), list));
+                    return item;
+                })
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 递归设置树形导航菜单
+     *
+     * @param parentId
+     * @param list
+     * @return
+     */
+    private List<SysNavMenuTreeVo> recursionSysNavMenuTreeList(Long parentId, List<SysNavMenuTreeVo> list) {
+        return list.stream()
+                .filter(vo -> vo.getParentId().equals(parentId))
+                .map(item -> {
+                    item.setChildren(recursionSysNavMenuTreeList(item.getId(), list));
+                    return item;
+                })
+                .collect(Collectors.toList());
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/impl/SysRoleMenuServiceImpl.java b/src/main/java/io/geekidea/boot/system/service/impl/SysRoleMenuServiceImpl.java
new file mode 100644
index 00000000..cbaeb3ee
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/impl/SysRoleMenuServiceImpl.java
@@ -0,0 +1,41 @@
+package io.geekidea.boot.system.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.system.entity.SysRoleMenu;
+import io.geekidea.boot.system.mapper.SysRoleMenuMapper;
+import io.geekidea.boot.system.service.SysRoleMenuService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 角色菜单关系表 服务实现类
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Slf4j
+@Service
+public class SysRoleMenuServiceImpl extends ServiceImpl<SysRoleMenuMapper, SysRoleMenu> implements SysRoleMenuService {
+
+    @Autowired
+    private SysRoleMenuMapper sysRoleMenuMapper;
+
+    @Override
+    public void deleteSysRoleMenuByRoleId(Long roleId) {
+        LambdaUpdateWrapper<SysRoleMenu> wrapper = new LambdaUpdateWrapper<>();
+        wrapper.eq(SysRoleMenu::getRoleId, roleId);
+        remove(wrapper);
+    }
+
+    @Override
+    public void deleteSysRoleMenuByMenuIds(List<Long> menuIds) {
+        LambdaUpdateWrapper<SysRoleMenu> wrapper = new LambdaUpdateWrapper<>();
+        wrapper.in(SysRoleMenu::getMenuId, menuIds);
+        remove(wrapper);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/impl/SysRoleServiceImpl.java b/src/main/java/io/geekidea/boot/system/service/impl/SysRoleServiceImpl.java
new file mode 100644
index 00000000..ce91859c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/impl/SysRoleServiceImpl.java
@@ -0,0 +1,210 @@
+package io.geekidea.boot.system.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.framework.page.OrderByItem;
+import io.geekidea.boot.framework.page.OrderMapping;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.system.dto.RoleMenusDto;
+import io.geekidea.boot.system.dto.SysRoleDto;
+import io.geekidea.boot.system.entity.SysMenu;
+import io.geekidea.boot.system.entity.SysRole;
+import io.geekidea.boot.system.entity.SysRoleMenu;
+import io.geekidea.boot.system.mapper.SysRoleMapper;
+import io.geekidea.boot.system.mapper.SysUserMapper;
+import io.geekidea.boot.system.query.SysRoleQuery;
+import io.geekidea.boot.system.service.SysMenuService;
+import io.geekidea.boot.system.service.SysRoleMenuService;
+import io.geekidea.boot.system.service.SysRoleService;
+import io.geekidea.boot.system.vo.SysRoleVo;
+import io.geekidea.boot.util.PagingUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 系统角色 服务实现类
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Slf4j
+@Service
+public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {
+
+    @Autowired
+    private SysRoleMapper sysRoleMapper;
+
+    @Autowired
+    private SysRoleMenuService sysRoleMenuService;
+
+    @Autowired
+    private SysMenuService sysMenuService;
+
+    @Autowired
+    private SysUserMapper sysUserMapper;
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean addSysRole(SysRoleDto dto) {
+        checkCodeExists(dto.getCode());
+        SysRole sysRole = new SysRole();
+        BeanUtils.copyProperties(dto, sysRole);
+        return save(sysRole);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updateSysRole(SysRoleDto dto) {
+        Long id = dto.getId();
+        SysRole sysRole = getById(id);
+        if (sysRole == null) {
+            throw new BusinessException("系统角色不存在");
+        }
+        sysRole.setName(dto.getName());
+        sysRole.setIsSystem(dto.getIsSystem());
+        sysRole.setRemark(dto.getRemark());
+        return updateById(sysRole);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean deleteSysRole(Long id) {
+        // 判断角色下是否存在用户，如果存在，则不能删除
+        Integer count = sysUserMapper.getCountByRoleId(id);
+        if (count > 0) {
+            throw new BusinessException("该角色下还存在用户，不可删除");
+        }
+        return removeById(id);
+    }
+
+    @Override
+    public SysRoleVo getSysRoleById(Long id) {
+        return sysRoleMapper.getSysRoleById(id);
+    }
+
+    @Override
+    public Paging<SysRoleVo> getSysRolePage(SysRoleQuery query) {
+        OrderMapping orderMapping = new OrderMapping();
+        orderMapping.put("createTime", "create_time");
+        PagingUtil.handlePage(query, orderMapping, OrderByItem.desc("id"));
+        List<SysRoleVo> list = sysRoleMapper.getSysRolePage(query);
+        Paging<SysRoleVo> paging = new Paging<>(list);
+        return paging;
+    }
+
+    @Override
+    public List<SysRole> getSysRoleAllList() {
+        LambdaQueryWrapper<SysRole> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.orderByAsc(SysRole::getId);
+        return list(lambdaQueryWrapper);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean setRoleMenus(RoleMenusDto roleMenusDto) {
+        Long roleId = roleMenusDto.getRoleId();
+        List<Long> menuIds = roleMenusDto.getMenuIds();
+        SysRole sysRole = getById(roleId);
+        if (sysRole == null) {
+            throw new BusinessException("角色不存在");
+        }
+        // 先删除角色权限关系
+        sysRoleMenuService.deleteSysRoleMenuByRoleId(roleId);
+        List<SysRoleMenu> sysRoleMenus = new ArrayList<>();
+        // 保存角色权限关系
+        for (Long menuId : menuIds) {
+            SysRoleMenu sysRoleMenu = new SysRoleMenu();
+            sysRoleMenu.setRoleId(roleId);
+            sysRoleMenu.setMenuId(menuId);
+            sysRoleMenu.setChoice(true);
+            sysRoleMenus.add(sysRoleMenu);
+        }
+        // 补全上级菜单父ID
+        Set<Long> completionMenuIds = completionMenuParentIds(menuIds);
+        if (CollectionUtils.isNotEmpty(completionMenuIds)) {
+            for (Long completionMenuId : completionMenuIds) {
+                SysRoleMenu sysRoleMenu = new SysRoleMenu();
+                sysRoleMenu.setRoleId(roleId);
+                sysRoleMenu.setMenuId(completionMenuId);
+                sysRoleMenu.setChoice(false);
+                sysRoleMenus.add(sysRoleMenu);
+            }
+        }
+        boolean flag = sysRoleMenuService.saveBatch(sysRoleMenus);
+        if (!flag) {
+            throw new BusinessException("设置角色权限异常");
+        }
+        return true;
+    }
+
+    @Override
+    public void checkCodeExists(String code) {
+        LambdaQueryWrapper<SysRole> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SysRole::getCode, code);
+        long count = count(wrapper);
+        if (count > 0) {
+            throw new BusinessException(code + "角色编码已经存在");
+        }
+    }
+
+    /**
+     * 补全菜单父ID
+     *
+     * @param choiceMenuIds
+     * @return
+     */
+    private Set<Long> completionMenuParentIds(List<Long> choiceMenuIds) {
+        // 获取所有菜单
+        List<SysMenu> menus = sysMenuService.list();
+        if (CollectionUtils.isEmpty(menus)) {
+            return null;
+        }
+        Map<Long, SysMenu> menuMap = menus.stream().collect(Collectors.toMap(SysMenu::getId, SysMenu -> SysMenu));
+        // 获取选择的菜单对象集合
+        List<SysMenu> choiceMenus = new ArrayList<>();
+        for (Long choiceMenuId : choiceMenuIds) {
+            SysMenu sysMenu = menuMap.get(choiceMenuId);
+            if (sysMenu != null) {
+                choiceMenus.add(sysMenu);
+            }
+        }
+        // 递归获取上级ID直到parentId为0截止
+        if (CollectionUtils.isEmpty(choiceMenus)) {
+            return null;
+        }
+        Set<Long> menuIdSet = new HashSet<>();
+        for (SysMenu choiceMenu : choiceMenus) {
+            recursionCompletionMenu(choiceMenuIds, menuIdSet, menuMap, choiceMenu);
+        }
+        return menuIdSet;
+    }
+
+    /**
+     * 递归补全菜单ID集合
+     *
+     * @param choiceMenuIds
+     * @param menuIdSet
+     * @param menuMap
+     * @param choiceMenu
+     */
+    private void recursionCompletionMenu(List<Long> choiceMenuIds, Set<Long> menuIdSet, Map<Long, SysMenu> menuMap, SysMenu choiceMenu) {
+        Long parentId = choiceMenu.getParentId();
+        // 判断是否存在此父ID，不存在则添加
+        if (!choiceMenuIds.contains(parentId) && !menuIdSet.contains(parentId)) {
+            if (parentId != 0) {
+                menuIdSet.add(parentId);
+                SysMenu parentMenu = menuMap.get(parentId);
+                recursionCompletionMenu(choiceMenuIds, menuIdSet, menuMap, parentMenu);
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/service/impl/SysUserServiceImpl.java b/src/main/java/io/geekidea/boot/system/service/impl/SysUserServiceImpl.java
new file mode 100644
index 00000000..4cbb391a
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/service/impl/SysUserServiceImpl.java
@@ -0,0 +1,218 @@
+package io.geekidea.boot.system.service.impl;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.read.listener.PageReadListener;
+import com.alibaba.fastjson2.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.auth.service.LoginRedisService;
+import io.geekidea.boot.auth.util.LoginUtil;
+import io.geekidea.boot.auth.util.TokenUtil;
+import io.geekidea.boot.common.constant.CommonConstant;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.framework.page.OrderByItem;
+import io.geekidea.boot.framework.page.OrderMapping;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.system.dto.*;
+import io.geekidea.boot.system.entity.SysUser;
+import io.geekidea.boot.system.mapper.SysUserMapper;
+import io.geekidea.boot.system.query.SysUserQuery;
+import io.geekidea.boot.system.service.SysUserService;
+import io.geekidea.boot.system.vo.SysUserVo;
+import io.geekidea.boot.util.PagingUtil;
+import io.geekidea.boot.util.PasswordUtil;
+import io.geekidea.boot.util.UUIDUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.InputStream;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 系统用户 服务实现类
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Slf4j
+@Service
+public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
+
+    @Autowired
+    private SysUserMapper sysUserMapper;
+
+    @Autowired
+    private LoginRedisService LoginRedisService;
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean addSysUser(SysUserDto dto) {
+        checkUsernameExists(dto.getUsername());
+        SysUser sysUser = new SysUser();
+        BeanUtils.copyProperties(dto, sysUser);
+        // 密码加盐
+        String salt = RandomStringUtils.randomNumeric(6);
+        sysUser.setSalt(salt);
+        String password = PasswordUtil.encrypt(sysUser.getPassword(), salt);
+        sysUser.setPassword(password);
+        // 保存用户
+        return save(sysUser);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updateSysUser(SysUserDto dto) {
+        Long id = dto.getId();
+        SysUser sysUser = getById(id);
+        if (sysUser == null) {
+            throw new BusinessException("系统用户不存在");
+        }
+        BeanUtils.copyProperties(dto, sysUser);
+        return updateById(sysUser);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean deleteSysUser(Long id) {
+        // 删除用户
+        return removeById(id);
+    }
+
+    @Override
+    public SysUserVo getSysUserById(Long id) {
+        return sysUserMapper.getSysUserById(id);
+    }
+
+    @Override
+    public Paging<SysUserVo> getSysUserPage(SysUserQuery query) {
+        OrderMapping orderMapping = new OrderMapping();
+        orderMapping.put("createTime", "create_time");
+        PagingUtil.handlePage(query, orderMapping, OrderByItem.desc("id"));
+        List<SysUserVo> list = sysUserMapper.getSysUserPage(query);
+        Paging<SysUserVo> paging = new Paging<>(list);
+        return paging;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean resetSysUserPassword(SysUserResetPasswordDto sysUserResetPasswordDto) {
+        Long userId = sysUserResetPasswordDto.getUserId();
+        log.info("管理员重置用户密码：" + userId);
+        SysUser sysUser = getById(userId);
+        if (sysUser == null) {
+            throw new BusinessException("系统用户不存在");
+        }
+        String password = sysUserResetPasswordDto.getPassword();
+        return handleUpdatePassword(userId, password);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updateProfile(SysUserUpdateProfileDto sysUserUpdateProfileDto) {
+        Long id = LoginUtil.getUserId();
+        SysUser sysUser = getById(id);
+        if (sysUser == null) {
+            throw new BusinessException("用户信息不存在");
+        }
+        LambdaUpdateWrapper<SysUser> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+        lambdaUpdateWrapper.set(SysUser::getNickname, sysUserUpdateProfileDto.getNickname());
+        lambdaUpdateWrapper.set(SysUser::getPhone, sysUserUpdateProfileDto.getPhone());
+        lambdaUpdateWrapper.set(SysUser::getEmail, sysUserUpdateProfileDto.getEmail());
+        lambdaUpdateWrapper.set(SysUser::getHead, sysUserUpdateProfileDto.getHead());
+        lambdaUpdateWrapper.eq(SysUser::getId, id);
+        boolean flag = update(lambdaUpdateWrapper);
+        return flag;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updatePassword(SysUserUpdatePasswordDto sysUserUpdatePasswordDto) {
+        Long id = LoginUtil.getUserId();
+        SysUser sysUser = getById(id);
+        if (sysUser == null) {
+            throw new BusinessException("用户信息不存在");
+        }
+        // 验证旧密码
+        String dbPassword = sysUser.getPassword();
+        String dbSalt = sysUser.getSalt();
+        String oldPassword = sysUserUpdatePasswordDto.getOldPassword();
+        String encryptOldPassword = PasswordUtil.encrypt(oldPassword, dbSalt);
+        if (!dbPassword.equals(encryptOldPassword)) {
+            throw new BusinessException("旧密码错误");
+        }
+        // 验证两次密码是否一致
+        String password = sysUserUpdatePasswordDto.getPassword();
+        String confirmPassword = sysUserUpdatePasswordDto.getConfirmPassword();
+        if (!password.equals(confirmPassword)) {
+            throw new BusinessException("两次输入的密码不一致");
+        }
+        // 新密码不能与旧密码一致
+        String newPassword = PasswordUtil.encrypt(password, dbSalt);
+        if (dbPassword.equals(newPassword)) {
+            throw new BusinessException("新密码不能与旧密码一致");
+        }
+        return handleUpdatePassword(id, password);
+    }
+
+    @Override
+    public void checkUsernameExists(String username) {
+        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SysUser::getUsername, username);
+        long count = count(wrapper);
+        if (count > 0) {
+            throw new BusinessException(username + "用户名已经存在");
+        }
+    }
+
+    @Override
+    public boolean importExcel(MultipartFile multipartFile) throws Exception {
+        String originalFilename = multipartFile.getOriginalFilename();
+        log.info("导入的Excel文件名称：" + originalFilename);
+        String extension = FilenameUtils.getExtension(originalFilename).toLowerCase();
+        if (!(extension.endsWith(CommonConstant.XLS) || extension.endsWith(CommonConstant.XLSX))) {
+            throw new BusinessException("只能导入xls或xlsx格式的Excel文件");
+        }
+        InputStream inputStream = multipartFile.getInputStream();
+
+        EasyExcel.read(inputStream, SysUserExcelDto.class, new PageReadListener<SysUserExcelDto>(dataList -> {
+            for (SysUserExcelDto demoData : dataList) {
+                log.info("读取到一条数据{}", JSON.toJSONString(demoData));
+            }
+        }, 10)).sheet().doRead();
+
+
+        return false;
+    }
+
+    /**
+     * 修改密码并删除该用户当前的登录信息
+     *
+     * @param id
+     * @param password
+     * @return
+     * @throws Exception
+     */
+    private boolean handleUpdatePassword(Long id, String password) {
+        // 生产新的盐值
+        String newSalt = UUIDUtil.getUuid();
+        String newPassword = PasswordUtil.encrypt(password, newSalt);
+        // 修改密码
+        LambdaUpdateWrapper<SysUser> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
+        lambdaUpdateWrapper.set(SysUser::getPassword, newPassword);
+        lambdaUpdateWrapper.set(SysUser::getSalt, newSalt);
+        lambdaUpdateWrapper.set(SysUser::getUpdateTime, new Date());
+        lambdaUpdateWrapper.eq(SysUser::getId, id);
+        // 清除当前用户登录信息
+        LoginRedisService.deleteLoginInfoByToken(TokenUtil.getToken());
+        return update(lambdaUpdateWrapper);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/vo/AppSysDictVo.java b/src/main/java/io/geekidea/boot/system/vo/AppSysDictVo.java
new file mode 100644
index 00000000..529c4481
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/vo/AppSysDictVo.java
@@ -0,0 +1,30 @@
+package io.geekidea.boot.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * App字典数据VO
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "App字典数据查询结果")
+public class AppSysDictVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "字典值")
+    private String value;
+
+    @Schema(description = "字典名称")
+    private String label;
+
+    @Schema(description = "字典类型code")
+    private String dictCode;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/vo/SysConfigVo.java b/src/main/java/io/geekidea/boot/system/vo/SysConfigVo.java
new file mode 100644
index 00000000..874eeac1
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/vo/SysConfigVo.java
@@ -0,0 +1,55 @@
+package io.geekidea.boot.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 系统配置VO
+ *
+ * @author geekidea
+ * @since 2023-11-27
+ */
+@Data
+@Schema(description = "系统配置查询结果")
+public class SysConfigVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "配置名称")
+    private String configName;
+
+    @Schema(description = "配置key")
+    private String configKey;
+
+    @Schema(description = "配置值")
+    private String configValue;
+
+    @Schema(description = "是否是系统字典类型")
+    private Boolean isSystem;
+
+    @Schema(description = "状态 1：正常，0：禁用")
+    private Boolean status;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "创建人ID")
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/vo/SysDictTypeVo.java b/src/main/java/io/geekidea/boot/system/vo/SysDictTypeVo.java
new file mode 100644
index 00000000..b468a2eb
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/vo/SysDictTypeVo.java
@@ -0,0 +1,37 @@
+package io.geekidea.boot.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 字典类型VO
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "字典类型查询结果")
+public class SysDictTypeVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "字典类型编码")
+    private String code;
+
+    @Schema(description = "字典类型名称")
+    private String name;
+
+    @Schema(description = "是否是系统字典类型")
+    private Boolean isSystem;
+
+    @Schema(description = "备注")
+    private String remark;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/vo/SysDictVo.java b/src/main/java/io/geekidea/boot/system/vo/SysDictVo.java
new file mode 100644
index 00000000..547798ef
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/vo/SysDictVo.java
@@ -0,0 +1,55 @@
+package io.geekidea.boot.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 字典数据VO
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "字典数据查询结果")
+public class SysDictVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "字典值")
+    private String value;
+
+    @Schema(description = "字典名称")
+    private String label;
+
+    @Schema(description = "字典类型code")
+    private String dictCode;
+
+    @Schema(description = "状态 1：启用，0：禁用")
+    private Boolean status;
+
+    @Schema(description = "排序值")
+    private Integer sort;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "创建人ID")
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/vo/SysFileVo.java b/src/main/java/io/geekidea/boot/system/vo/SysFileVo.java
new file mode 100644
index 00000000..bd9923cc
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/vo/SysFileVo.java
@@ -0,0 +1,83 @@
+package io.geekidea.boot.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 系统文件VO
+ *
+ * @author geekidea
+ * @since 2023-11-26
+ */
+@Data
+@Schema(description = "系统文件查询结果")
+public class SysFileVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "日志链路ID")
+    private String traceId;
+
+    @Schema(description = "服务类型 1：本地服务，2：阿里云OSS")
+    private Integer serverType;
+
+    @Schema(description = "上传类型")
+    private String uploadType;
+
+    @Schema(description = "目录名称")
+    private String dirName;
+
+    @Schema(description = "源文件名称")
+    private String originalFileName;
+
+    @Schema(description = "生成的文件名称")
+    private String fileName;
+
+    @Schema(description = "文件内容类型")
+    private String contentType;
+
+    @Schema(description = "文件后缀")
+    private String extension;
+
+    @Schema(description = "文件大小")
+    private Long size;
+
+    @Schema(description = "文件大小MB")
+    private BigDecimal sizeMb;
+
+    @Schema(description = "访问的URL")
+    private String url;
+
+    @Schema(description = "系统类型 1：管理端，2：移动端")
+    private Integer systemType;
+
+    @Schema(description = "用户ID")
+    private Long userId;
+
+    @Schema(description = "本地文件服务时的物流文件位置")
+    private String filePath;
+
+    @Schema(description = "文件类型 1：图片，2：音视频，3：文档，4：文件")
+    private Integer fileType;
+
+    @Schema(description = "创建人ID")
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/vo/SysLogVo.java b/src/main/java/io/geekidea/boot/system/vo/SysLogVo.java
new file mode 100644
index 00000000..9f5ae8df
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/vo/SysLogVo.java
@@ -0,0 +1,104 @@
+package io.geekidea.boot.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 系统日志VO
+ *
+ * @author geekidea
+ * @since 2023-02-16
+ */
+@Data
+@Schema(description = "系统日志查询结果")
+public class SysLogVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "日志链路ID")
+    private String traceId;
+
+    @Schema(description = "请求时间")
+    private String requestTime;
+
+    @Schema(description = "全路径")
+    private String requestUrl;
+
+    @Schema(description = "日志名称")
+    private String logName;
+
+    @Schema(description = "请求方式，GET/POST")
+    private String requestMethod;
+
+    @Schema(description = "模块名称")
+    private String moduleName;
+
+    @Schema(description = "请求参数")
+    private String requestParam;
+
+    @Schema(description = "用户名")
+    private String username;
+
+    @Schema(description = "请求ip")
+    private String requestIp;
+
+    @Schema(description = "IP区域描述")
+    private String ipAreaDesc;
+
+    @Schema(description = "0:访问日志,1:新增,2:修改,3:删除,4:详情,5:所有列表,6:分页列表,7:其它查询,8:上传文件,9:登录,10:退出")
+    private Integer logType;
+
+    @Schema(description = "响应时间")
+    private String responseTime;
+
+    @Schema(description = "0:失败,1:成功")
+    private Boolean responseSuccess;
+
+    @Schema(description = "响应结果状态码")
+    private Integer responseCode;
+
+    @Schema(description = "响应结果消息")
+    private String responseMessage;
+
+    @Schema(description = "异常类名称")
+    private String exceptionName;
+
+    @Schema(description = "异常信息")
+    private String exceptionMessage;
+
+    @Schema(description = "耗时，单位：毫秒")
+    private Long diffTime;
+
+    @Schema(description = "耗时描述")
+    private String diffTimeDesc;
+
+    @Schema(description = "是否手机 0：否，1：是")
+    private Boolean isMobile;
+
+    @Schema(description = "平台名称")
+    private String platformName;
+
+    @Schema(description = "浏览器名称")
+    private String browserName;
+
+    @Schema(description = "创建人ID")
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    private Date updateTime;
+
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/vo/SysMenuTreeVo.java b/src/main/java/io/geekidea/boot/system/vo/SysMenuTreeVo.java
new file mode 100644
index 00000000..3b0eec1a
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/vo/SysMenuTreeVo.java
@@ -0,0 +1,52 @@
+package io.geekidea.boot.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2023/6/18
+ **/
+@Data
+@Schema(description = "菜单树形列表VO")
+public class SysMenuTreeVo {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "菜单名称")
+    private String name;
+
+    @Schema(description = "父id")
+    private Long parentId;
+
+    @Schema(description = "菜单编码")
+    private String code;
+
+    @Schema(description = "菜单图标")
+    private String icon;
+
+    @Schema(description = "菜单类型，1：目录，2：菜单，3：权限")
+    private Integer type;
+
+    @Schema(description = "状态，0：禁用，1：启用")
+    private Boolean status;
+
+    @Schema(description = "排序")
+    private Integer sort;
+
+    @Schema(description = "前端路由地址")
+    private String routeUrl;
+
+    @Schema(description = "组件路径")
+    private String componentPath;
+
+    @Schema(description = "子菜单集合")
+    private List<SysMenuTreeVo> children;
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/vo/SysMenuVo.java b/src/main/java/io/geekidea/boot/system/vo/SysMenuVo.java
new file mode 100644
index 00000000..143d05ed
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/vo/SysMenuVo.java
@@ -0,0 +1,73 @@
+package io.geekidea.boot.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 系统菜单VO
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@Schema(description = "系统菜单查询结果")
+public class SysMenuVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "菜单名称")
+    private String name;
+
+    @Schema(description = "父id")
+    private Long parentId;
+
+    @Schema(description = "菜单编码")
+    private String code;
+
+    @Schema(description = "菜单图标")
+    private String icon;
+
+    @Schema(description = "菜单类型，1：目录，2：菜单，3：权限")
+    private Integer type;
+
+    @Schema(description = "状态，0：禁用，1：启用")
+    private Boolean status;
+
+    @Schema(description = "排序")
+    private Integer sort;
+
+    @Schema(description = "前端路由地址")
+    private String routeUrl;
+
+    @Schema(description = "重定向")
+    private String routeRedirect;
+
+    @Schema(description = "组件路径")
+    private String componentPath;
+
+    @Schema(description = "是否显示,0：不显示，1：显示")
+    private Boolean isShow;
+
+    @Schema(description = "是否缓存，0：否 1：是")
+    private Boolean isCache;
+
+    @Schema(description = "创建人ID")
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/vo/SysNavMenuTreeVo.java b/src/main/java/io/geekidea/boot/system/vo/SysNavMenuTreeVo.java
new file mode 100644
index 00000000..52143652
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/vo/SysNavMenuTreeVo.java
@@ -0,0 +1,73 @@
+package io.geekidea.boot.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2023/6/18
+ **/
+@Data
+@Schema(description = "用户导航菜单树形列表VO")
+public class SysNavMenuTreeVo {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "菜单名称")
+    private String name;
+
+    @Schema(description = "父id")
+    private Long parentId;
+
+    @Schema(description = "菜单编码")
+    private String code;
+
+    @Schema(description = "菜单图标")
+    private String icon;
+
+    @Schema(description = "菜单类型，1：目录，2：菜单，3：权限")
+    private Integer type;
+
+    @Schema(description = "状态，0：禁用，1：启用")
+    private Boolean status;
+
+    @Schema(description = "排序")
+    private Integer sort;
+
+    @Schema(description = "前端路由地址")
+    private String routeUrl;
+
+    @Schema(description = "重定向")
+    private String routeRedirect;
+
+    @Schema(description = "组件路径")
+    private String componentPath;
+
+    @Schema(description = "是否显示,0：不显示，1：显示")
+    private Boolean isShow;
+
+    @Schema(description = "是否缓存，0：否 1：是")
+    private Boolean isCache;
+
+    @Schema(description = "创建人ID")
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    private Date updateTime;
+
+    @Schema(description = "子菜单集合")
+    private List<SysNavMenuTreeVo> children;
+
+}
diff --git a/src/main/java/io/geekidea/boot/system/vo/SysRoleVo.java b/src/main/java/io/geekidea/boot/system/vo/SysRoleVo.java
new file mode 100644
index 00000000..f2d8802e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/vo/SysRoleVo.java
@@ -0,0 +1,49 @@
+package io.geekidea.boot.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 系统角色VO
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@Schema(description = "系统角色查询结果")
+public class SysRoleVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "角色名称")
+    private String name;
+
+    @Schema(description = "角色唯一编码")
+    private String code;
+
+    @Schema(description = "是否系统内置角色 1：是，0：否")
+    private Boolean isSystem;
+
+    @Schema(description = "角色备注")
+    private String remark;
+
+    @Schema(description = "创建人ID")
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/system/vo/SysUserVo.java b/src/main/java/io/geekidea/boot/system/vo/SysUserVo.java
new file mode 100644
index 00000000..690f9cd2
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/system/vo/SysUserVo.java
@@ -0,0 +1,67 @@
+package io.geekidea.boot.system.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 系统用户VO
+ *
+ * @author geekidea
+ * @since 2022-12-26
+ */
+@Data
+@Schema(description = "系统用户查询结果")
+public class SysUserVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "用户名")
+    private String username;
+
+    @Schema(description = "昵称")
+    private String nickname;
+
+    @Schema(description = "手机号码")
+    private String phone;
+
+    @Schema(description = "电子邮件")
+    private String email;
+
+    @Schema(description = "头像")
+    private String head;
+
+    @Schema(description = "状态，0：禁用，1：启用")
+    private Boolean status;
+
+    @Schema(description = "角色ID")
+    private Long roleId;
+
+    @Schema(description = "角色名称")
+    private String roleName;
+
+    @Schema(description = "是否系统内置角色 1：是，0：否")
+    private String roleIsSystem;
+
+    @Schema(description = "是否是超管 0：否，1：是")
+    private Boolean isAdmin;
+
+    @Schema(description = "创建人ID")
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/user/controller/AppUserController.java b/src/main/java/io/geekidea/boot/user/controller/AppUserController.java
new file mode 100644
index 00000000..03efa782
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/controller/AppUserController.java
@@ -0,0 +1,75 @@
+package io.geekidea.boot.user.controller;
+
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.user.dto.AppUserHeadDto;
+import io.geekidea.boot.user.dto.AppUserNicknameDto;
+import io.geekidea.boot.user.service.UserService;
+import io.geekidea.boot.user.vo.AppUserVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+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 javax.validation.Valid;
+
+/**
+ * App用户信息 控制器
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Slf4j
+@RestController
+@Tag(name = "App用户信息")
+@RequestMapping("/app/user")
+public class AppUserController {
+
+    @Autowired
+    private UserService userService;
+
+    /**
+     * 获取App用户信息
+     *
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getProfile")
+    @Operation(summary = "获取App用户信息")
+    public ApiResult<AppUserVo> getProfile() {
+        AppUserVo appUserVo = userService.getProfile();
+        return ApiResult.success(appUserVo);
+    }
+
+    /**
+     * 修改用户头像
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/updateHead")
+    @Operation(summary = "修改用户头像")
+    public ApiResult<Boolean> updateHead(@Valid @RequestBody AppUserHeadDto dto) {
+        boolean flag = userService.updateHead(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 修改昵称
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/updateNickname")
+    @Operation(summary = "修改昵称")
+    public ApiResult<AppUserVo> updateNickname(@Valid @RequestBody AppUserNicknameDto dto) {
+        boolean flag = userService.updateNickname(dto);
+        return ApiResult.result(flag);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/user/controller/UserController.java b/src/main/java/io/geekidea/boot/user/controller/UserController.java
new file mode 100644
index 00000000..bb613193
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/controller/UserController.java
@@ -0,0 +1,113 @@
+package io.geekidea.boot.user.controller;
+
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.common.enums.SysLogType;
+import io.geekidea.boot.framework.annotation.Log;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.user.dto.UserDto;
+import io.geekidea.boot.user.query.UserQuery;
+import io.geekidea.boot.user.service.UserService;
+import io.geekidea.boot.user.vo.UserVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+/**
+ * 用户信息 控制器
+ *
+ * @author geekidea
+ * @since 2023-11-30
+ */
+@Slf4j
+@RestController
+@Tag(name = "用户信息")
+@RequestMapping("/admin/user")
+public class UserController {
+
+    @Autowired
+    private UserService userService;
+
+    /**
+     * 添加用户信息
+     *
+     * @param userDto
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "添加用户信息", type = SysLogType.ADD)
+    @PostMapping("/addUser")
+    @Operation(summary = "添加用户信息")
+    @Permission("user:add")
+    public ApiResult addUser(@Valid @RequestBody UserDto userDto) {
+        boolean flag = userService.addUser(userDto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 修改用户信息
+     *
+     * @param userDto
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "修改用户信息", type = SysLogType.UPDATE)
+    @PostMapping("/updateUser")
+    @Operation(summary = "修改用户信息")
+    @Permission("user:update")
+    public ApiResult updateUser(@Valid @RequestBody UserDto userDto) {
+        boolean flag = userService.updateUser(userDto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 删除用户信息
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "删除用户信息", type = SysLogType.DELETE)
+    @PostMapping("/deleteUser/{id}")
+    @Operation(summary = "删除用户信息")
+    @Permission("user:delete")
+    public ApiResult deleteUser(@PathVariable Long id) {
+        boolean flag = userService.deleteUser(id);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 获取用户信息详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getUser/{id}")
+    @Operation(summary = "用户信息详情")
+    @Permission("user:info")
+    public ApiResult<UserVo> getUser(@PathVariable Long id) {
+        UserVo userVo = userService.getUserById(id);
+        return ApiResult.success(userVo);
+    }
+
+    /**
+     * 用户信息分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    @PostMapping("/getUserPage")
+    @Operation(summary = "用户信息分页列表")
+    @Permission("user:page")
+    public ApiResult<UserVo> getUserPage(@Valid @RequestBody UserQuery query) {
+        Paging<UserVo> paging = userService.getUserPage(query);
+        return ApiResult.success(paging);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/user/controller/UserRoleController.java b/src/main/java/io/geekidea/boot/user/controller/UserRoleController.java
new file mode 100644
index 00000000..6ed4064f
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/controller/UserRoleController.java
@@ -0,0 +1,118 @@
+package io.geekidea.boot.user.controller;
+
+import io.geekidea.boot.auth.annotation.Permission;
+import io.geekidea.boot.common.enums.SysLogType;
+import io.geekidea.boot.framework.annotation.Log;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.framework.response.ApiResult;
+import io.geekidea.boot.user.dto.UserRoleDto;
+import io.geekidea.boot.user.query.UserRoleQuery;
+import io.geekidea.boot.user.service.UserRoleService;
+import io.geekidea.boot.user.vo.UserRoleVo;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+/**
+ * 用户角色 控制器
+ *
+ * @author geekidea
+ * @since 2024-01-06
+ */
+@Slf4j
+@RestController
+@Tag(name = "用户角色")
+@RequestMapping("/admin/userRole")
+public class UserRoleController {
+
+    @Autowired
+    private UserRoleService userRoleService;
+
+    /**
+     * 添加用户角色
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "添加用户角色", type = SysLogType.ADD)
+    @Operation(summary = "添加用户角色")
+    @PostMapping("/addUserRole")
+    @Permission("user:role:add")
+    public ApiResult addUserRole(@Valid @RequestBody UserRoleDto dto) {
+        log.info("添加用户角色：{}", dto);
+        boolean flag = userRoleService.addUserRole(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 修改用户角色
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "修改用户角色", type = SysLogType.UPDATE)
+    @Operation(summary = "修改用户角色")
+    @PostMapping("/updateUserRole")
+    @Permission("user:role:update")
+    public ApiResult updateUserRole(@Valid @RequestBody UserRoleDto dto) {
+        log.info("修改用户角色：{}", dto);
+        boolean flag = userRoleService.updateUserRole(dto);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 删除用户角色
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @Log(value = "删除用户角色", type = SysLogType.DELETE)
+    @Operation(summary = "删除用户角色")
+    @PostMapping("/deleteUserRole/{id}")
+    @Permission("user:role:delete")
+    public ApiResult deleteUserRole(@PathVariable Long id) {
+        log.info("删除用户角色：{}", id);
+        boolean flag = userRoleService.deleteUserRole(id);
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * 用户角色详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    @Operation(summary = "用户角色详情")
+    @PostMapping("/getUserRole/{id}")
+    @Permission("user:role:info")
+    public ApiResult<UserRoleVo> getUserRole(@PathVariable Long id) {
+        log.info("用户角色详情：{}", id);
+        UserRoleVo userRoleVo = userRoleService.getUserRoleById(id);
+        return ApiResult.success(userRoleVo);
+    }
+
+    /**
+     * 用户角色分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    @Operation(summary = "用户角色分页列表")
+    @PostMapping("/getUserRolePage")
+    @Permission("user:role:page")
+    public ApiResult<UserRoleVo> getUserRolePage(@Valid @RequestBody UserRoleQuery query) {
+        log.info("用户角色分页列表：{}", query);
+        Paging<UserRoleVo> paging = userRoleService.getUserRolePage(query);
+        return ApiResult.success(paging);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/user/dto/AppUserHeadDto.java b/src/main/java/io/geekidea/boot/user/dto/AppUserHeadDto.java
new file mode 100644
index 00000000..0ba1c0f1
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/dto/AppUserHeadDto.java
@@ -0,0 +1,27 @@
+package io.geekidea.boot.user.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+/**
+ * App修改用户头像参数
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "App修改用户头像参数")
+public class AppUserHeadDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "头像")
+    @NotBlank(message = "头像不能为空")
+    private String head;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/user/dto/AppUserNicknameDto.java b/src/main/java/io/geekidea/boot/user/dto/AppUserNicknameDto.java
new file mode 100644
index 00000000..d7ce85dd
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/dto/AppUserNicknameDto.java
@@ -0,0 +1,27 @@
+package io.geekidea.boot.user.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+/**
+ * App修改用户昵称参数
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "App修改用户昵称参数")
+public class AppUserNicknameDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "昵称")
+    @NotBlank(message = "昵称不能为空")
+    private String nickname;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/user/dto/UserDto.java b/src/main/java/io/geekidea/boot/user/dto/UserDto.java
new file mode 100644
index 00000000..d7de8b09
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/dto/UserDto.java
@@ -0,0 +1,83 @@
+package io.geekidea.boot.user.dto;
+
+import java.util.Date;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * 修改用户信息参数
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "用户信息参数")
+public class UserDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "ID")
+    private Long id;
+
+    @Schema(description = "账号")
+    @Length(max = 20, message = "账号长度超过限制")
+    private String username;
+
+    @Schema(description = "昵称")
+    @Length(max = 20, message = "昵称长度超过限制")
+    private String nickname;
+
+    @Schema(description = "密码")
+    @Length(max = 64, message = "密码长度超过限制")
+    private String password;
+
+    @Schema(description = "盐值")
+    @Length(max = 32, message = "盐值长度超过限制")
+    private String salt;
+
+    @Schema(description = "微信openid")
+    @Length(max = 200, message = "微信openid长度超过限制")
+    private String openid;
+
+    @Schema(description = "手机号码")
+    @Length(max = 11, message = "手机号码长度超过限制")
+    private String phone;
+
+    @Schema(description = "头像")
+    @Length(max = 200, message = "头像长度超过限制")
+    private String head;
+
+    @Schema(description = "用户角色ID")
+    private Long userRoleId;
+
+    @Schema(description = "状态 1：正常，0：禁用")
+    @NotNull(message = "状态 1：正常，0：禁用不能为空")
+    private Boolean status;
+
+    @Schema(description = "注册时间")
+    @NotNull(message = "注册时间不能为空")
+    private Date registerTime;
+
+    @Schema(description = "注册IP")
+    @NotBlank(message = "注册IP不能为空")
+    @Length(max = 20, message = "注册IP长度超过限制")
+    private String registerIp;
+
+    @Schema(description = "最后登录时间")
+    @NotNull(message = "最后登录时间不能为空")
+    private Date lastLoginTime;
+
+    @Schema(description = "最后登录IP")
+    @NotBlank(message = "最后登录IP不能为空")
+    @Length(max = 20, message = "最后登录IP长度超过限制")
+    private String lastLoginIp;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/user/dto/UserRoleDto.java b/src/main/java/io/geekidea/boot/user/dto/UserRoleDto.java
new file mode 100644
index 00000000..14f7a788
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/dto/UserRoleDto.java
@@ -0,0 +1,42 @@
+package io.geekidea.boot.user.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 用户角色参数
+ *
+ * @author geekidea
+ * @since 2024-01-06
+ */
+@Data
+@Schema(description = "用户角色参数")
+public class UserRoleDto implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "用户角色唯一编码")
+    @Length(max = 100, message = "用户角色唯一编码长度超过限制")
+    private String code;
+
+    @Schema(description = "用户角色名称")
+    @NotBlank(message = "用户角色名称不能为空")
+    @Length(max = 32, message = "用户角色名称长度超过限制")
+    private String name;
+
+    @Schema(description = "备注")
+    @Length(max = 200, message = "备注长度超过限制")
+    private String remark;
+
+}
+
+
diff --git a/src/main/java/io/geekidea/boot/user/entity/User.java b/src/main/java/io/geekidea/boot/user/entity/User.java
new file mode 100644
index 00000000..1886a049
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/entity/User.java
@@ -0,0 +1,92 @@
+package io.geekidea.boot.user.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+
+import java.io.Serializable;
+import java.util.Date;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 用户信息
+ *
+ * @author geekidea
+ * @since 2023-11-30
+ */
+@Data
+@TableName("user")
+@Schema(description = "用户信息")
+public class User implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "ID")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "账号")
+    private String username;
+
+    @Schema(description = "昵称")
+    private String nickname;
+
+    @Schema(description = "密码")
+    private String password;
+
+    @Schema(description = "盐值")
+    private String salt;
+
+    @Schema(description = "微信openid")
+    private String openid;
+
+    @Schema(description = "手机号码")
+    private String phone;
+
+    @Schema(description = "头像")
+    private String head;
+
+    @Schema(description = "用户角色ID")
+    private Long userRoleId;
+
+    @Schema(description = "状态 1：正常，0：禁用")
+    private Boolean status;
+
+    @Schema(description = "注册时间")
+    private Date registerTime;
+
+    @Schema(description = "注册IP")
+    private String registerIp;
+
+    @Schema(description = "注册IP区域")
+    private String registerIpArea;
+
+    @Schema(description = "最后登录时间")
+    private Date lastLoginTime;
+
+    @Schema(description = "最后登录IP")
+    private String lastLoginIp;
+
+    @Schema(description = "最后一次登录IP区域")
+    private String lastLoginIpArea;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/user/entity/UserRole.java b/src/main/java/io/geekidea/boot/user/entity/UserRole.java
new file mode 100644
index 00000000..b76eff5e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/entity/UserRole.java
@@ -0,0 +1,53 @@
+package io.geekidea.boot.user.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 用户角色
+ *
+ * @author geekidea
+ * @since 2024-01-06
+ */
+@Data
+@TableName("user_role")
+@Schema(description = "用户角色")
+public class UserRole implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    private Long id;
+
+    @Schema(description = "用户角色唯一编码")
+    private String code;
+
+    @Schema(description = "用户角色名称")
+    private String name;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "创建人ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long createId;
+
+    @Schema(description = "修改人ID")
+    @TableField(fill = FieldFill.UPDATE)
+    private Long updateId;
+
+    @Schema(description = "创建时间")
+    @TableField(fill = FieldFill.INSERT)
+    private Date createTime;
+
+    @Schema(description = "修改时间")
+    @TableField(fill = FieldFill.UPDATE)
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/user/mapper/UserMapper.java b/src/main/java/io/geekidea/boot/user/mapper/UserMapper.java
new file mode 100644
index 00000000..3a3cbbd0
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/mapper/UserMapper.java
@@ -0,0 +1,54 @@
+package io.geekidea.boot.user.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.user.entity.User;
+import io.geekidea.boot.user.query.UserQuery;
+import io.geekidea.boot.user.vo.UserVo;
+import io.geekidea.boot.user.query.AppUserQuery;
+import io.geekidea.boot.user.vo.AppUserVo;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 用户信息 Mapper 接口
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Mapper
+public interface UserMapper extends BaseMapper<User> {
+
+    /**
+     * 用户信息详情
+     *
+     * @param id
+     * @return
+     */
+    UserVo getUserById(Long id);
+
+    /**
+     * 用户信息分页列表
+     *
+     * @param query
+     * @return
+     */
+    List<UserVo> getUserPage(UserQuery query);
+
+    /**
+     * App用户信息详情
+     *
+     * @param id
+     * @return
+     */
+    AppUserVo getAppUserById(Long id);
+
+    /**
+     * App用户信息分页列表
+     *
+     * @param query
+     * @return
+     */
+    List<AppUserVo> getAppUserPage(AppUserQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/user/mapper/UserRoleMapper.java b/src/main/java/io/geekidea/boot/user/mapper/UserRoleMapper.java
new file mode 100644
index 00000000..7351bf34
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/mapper/UserRoleMapper.java
@@ -0,0 +1,36 @@
+package io.geekidea.boot.user.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import io.geekidea.boot.user.entity.UserRole;
+import io.geekidea.boot.user.query.UserRoleQuery;
+import io.geekidea.boot.user.vo.UserRoleVo;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 用户角色 Mapper接口
+ *
+ * @author geekidea
+ * @since 2024-01-06
+ */
+@Mapper
+public interface UserRoleMapper extends BaseMapper<UserRole> {
+
+    /**
+     * 用户角色详情
+     *
+     * @param id
+     * @return
+     */
+    UserRoleVo getUserRoleById(Long id);
+
+    /**
+     * 用户角色分页列表
+     *
+     * @param query
+     * @return
+     */
+    List<UserRoleVo> getUserRolePage(UserRoleQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/user/query/AppUserQuery.java b/src/main/java/io/geekidea/boot/user/query/AppUserQuery.java
new file mode 100644
index 00000000..73423ddc
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/query/AppUserQuery.java
@@ -0,0 +1,19 @@
+package io.geekidea.boot.user.query;
+
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * App用户信息查询参数
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "App用户信息查询参数")
+public class AppUserQuery extends BasePageQuery {
+    private static final long serialVersionUID = 1L;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/user/query/UserQuery.java b/src/main/java/io/geekidea/boot/user/query/UserQuery.java
new file mode 100644
index 00000000..0a2795ba
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/query/UserQuery.java
@@ -0,0 +1,19 @@
+package io.geekidea.boot.user.query;
+
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 用户信息查询参数
+ *
+ * @author geekidea
+ * @since 2023-11-30
+ */
+@Data
+@Schema(description = "用户信息查询参数")
+public class UserQuery extends BasePageQuery {
+    private static final long serialVersionUID = 1L;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/user/query/UserRoleQuery.java b/src/main/java/io/geekidea/boot/user/query/UserRoleQuery.java
new file mode 100644
index 00000000..5de29c84
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/query/UserRoleQuery.java
@@ -0,0 +1,20 @@
+package io.geekidea.boot.user.query;
+
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 用户角色查询参数
+ *
+ * @author geekidea
+ * @since 2024-01-06
+ */
+@Data
+@Schema(description = "用户角色查询参数")
+public class UserRoleQuery extends BasePageQuery {
+
+    private static final long serialVersionUID = 1L;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/user/service/UserRoleService.java b/src/main/java/io/geekidea/boot/user/service/UserRoleService.java
new file mode 100644
index 00000000..a608c38d
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/service/UserRoleService.java
@@ -0,0 +1,63 @@
+package io.geekidea.boot.user.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.user.dto.UserRoleDto;
+import io.geekidea.boot.user.entity.UserRole;
+import io.geekidea.boot.user.query.UserRoleQuery;
+import io.geekidea.boot.user.vo.UserRoleVo;
+
+/**
+ * 用户角色 服务接口
+ *
+ * @author geekidea
+ * @since 2024-01-06
+ */
+public interface UserRoleService extends IService<UserRole> {
+
+    /**
+     * 添加用户角色
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean addUserRole(UserRoleDto dto);
+
+    /**
+     * 修改用户角色
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean updateUserRole(UserRoleDto dto);
+
+    /**
+     * 删除用户角色
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    boolean deleteUserRole(Long id);
+
+    /**
+     * 用户角色详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    UserRoleVo getUserRoleById(Long id);
+
+    /**
+     * 用户角色分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    Paging<UserRoleVo> getUserRolePage(UserRoleQuery query);
+
+}
diff --git a/src/main/java/io/geekidea/boot/user/service/UserService.java b/src/main/java/io/geekidea/boot/user/service/UserService.java
new file mode 100644
index 00000000..7d009f0c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/service/UserService.java
@@ -0,0 +1,111 @@
+package io.geekidea.boot.user.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.user.dto.AppUserHeadDto;
+import io.geekidea.boot.user.dto.AppUserNicknameDto;
+import io.geekidea.boot.user.dto.UserDto;
+import io.geekidea.boot.user.entity.User;
+import io.geekidea.boot.user.query.UserQuery;
+import io.geekidea.boot.user.vo.AppUserVo;
+import io.geekidea.boot.user.vo.UserVo;
+
+
+/**
+ * 用户信息 服务接口
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+public interface UserService extends IService<User> {
+
+    /**
+     * 根据微信openid获取用户
+     *
+     * @param openid
+     * @return
+     * @throws Exception
+     */
+    User getUserByOpenid(String openid);
+
+    /**
+     * 根据账号获取用户
+     *
+     * @param username
+     * @return
+     * @throws Exception
+     */
+    User getUserByUsername(String username);
+
+    /**
+     * 添加用户信息
+     *
+     * @param userDto
+     * @return
+     * @throws Exception
+     */
+    boolean addUser(UserDto userDto);
+
+    /**
+     * 修改用户信息
+     *
+     * @param userDto
+     * @return
+     * @throws Exception
+     */
+    boolean updateUser(UserDto userDto);
+
+    /**
+     * 删除用户信息
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    boolean deleteUser(Long id);
+
+    /**
+     * 用户信息详情
+     *
+     * @param id
+     * @return
+     * @throws Exception
+     */
+    UserVo getUserById(Long id);
+
+    /**
+     * 用户信息分页列表
+     *
+     * @param query
+     * @return
+     * @throws Exception
+     */
+    Paging<UserVo> getUserPage(UserQuery query);
+
+    /**
+     * 获取App用户信息
+     *
+     * @return
+     * @throws Exception
+     */
+    AppUserVo getProfile();
+
+    /**
+     * 修改用户头像
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean updateHead(AppUserHeadDto dto);
+
+    /**
+     * 修改用户昵称
+     *
+     * @param dto
+     * @return
+     * @throws Exception
+     */
+    boolean updateNickname(AppUserNicknameDto dto);
+
+}
diff --git a/src/main/java/io/geekidea/boot/user/service/impl/UserRoleServiceImpl.java b/src/main/java/io/geekidea/boot/user/service/impl/UserRoleServiceImpl.java
new file mode 100644
index 00000000..735cbfe2
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/service/impl/UserRoleServiceImpl.java
@@ -0,0 +1,77 @@
+package io.geekidea.boot.user.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.framework.page.OrderByItem;
+import io.geekidea.boot.framework.page.OrderMapping;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.user.dto.UserRoleDto;
+import io.geekidea.boot.user.entity.UserRole;
+import io.geekidea.boot.user.mapper.UserRoleMapper;
+import io.geekidea.boot.user.query.UserRoleQuery;
+import io.geekidea.boot.user.service.UserRoleService;
+import io.geekidea.boot.user.vo.UserRoleVo;
+import io.geekidea.boot.util.PagingUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 用户角色 服务实现类
+ *
+ * @author geekidea
+ * @since 2024-01-06
+ */
+@Slf4j
+@Service
+public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> implements UserRoleService {
+
+    @Autowired
+    private UserRoleMapper userRoleMapper;
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean addUserRole(UserRoleDto dto) {
+        UserRole userRole = new UserRole();
+        BeanUtils.copyProperties(dto, userRole);
+        return save(userRole);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updateUserRole(UserRoleDto dto) {
+        Long id = dto.getId();
+        UserRole userRole = getById(id);
+        if (userRole == null) {
+            throw new BusinessException("用户角色不存在");
+        }
+        BeanUtils.copyProperties(dto, userRole);
+        return updateById(userRole);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean deleteUserRole(Long id) {
+        return removeById(id);
+    }
+
+    @Override
+    public UserRoleVo getUserRoleById(Long id) {
+        return userRoleMapper.getUserRoleById(id);
+    }
+
+    @Override
+    public Paging<UserRoleVo> getUserRolePage(UserRoleQuery query) {
+        OrderMapping orderMapping = new OrderMapping();
+        orderMapping.put("createTime", "create_time");
+        PagingUtil.handlePage(query, orderMapping, OrderByItem.desc("id"));
+        List<UserRoleVo> list = userRoleMapper.getUserRolePage(query);
+        Paging<UserRoleVo> paging = new Paging<>(list);
+        return paging;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/user/service/impl/UserServiceImpl.java b/src/main/java/io/geekidea/boot/user/service/impl/UserServiceImpl.java
new file mode 100644
index 00000000..f108f64a
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/service/impl/UserServiceImpl.java
@@ -0,0 +1,121 @@
+package io.geekidea.boot.user.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import io.geekidea.boot.auth.util.AppLoginUtil;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.framework.page.OrderByItem;
+import io.geekidea.boot.framework.page.OrderMapping;
+import io.geekidea.boot.framework.page.Paging;
+import io.geekidea.boot.user.dto.AppUserHeadDto;
+import io.geekidea.boot.user.dto.AppUserNicknameDto;
+import io.geekidea.boot.user.dto.UserDto;
+import io.geekidea.boot.user.entity.User;
+import io.geekidea.boot.user.mapper.UserMapper;
+import io.geekidea.boot.user.query.UserQuery;
+import io.geekidea.boot.user.service.UserService;
+import io.geekidea.boot.user.vo.AppUserVo;
+import io.geekidea.boot.user.vo.UserVo;
+import io.geekidea.boot.util.PagingUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 用户信息 服务实现类
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Slf4j
+@Service
+public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
+
+    @Autowired
+    private UserMapper userMapper;
+
+    @Override
+    public User getUserByOpenid(String openid) {
+        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(User::getOpenid, openid);
+        return getOne(wrapper);
+    }
+
+    @Override
+    public User getUserByUsername(String username) {
+        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(User::getUsername, username);
+        return getOne(wrapper);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean addUser(UserDto userDto) {
+        User user = new User();
+        BeanUtils.copyProperties(userDto, user);
+        return save(user);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean updateUser(UserDto userDto) {
+        Long id = userDto.getId();
+        User user = getById(id);
+        if (user == null) {
+            throw new BusinessException("用户信息不存在");
+        }
+        BeanUtils.copyProperties(userDto, user);
+        return updateById(user);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean deleteUser(Long id) {
+        return removeById(id);
+    }
+
+    @Override
+    public UserVo getUserById(Long id) {
+        return userMapper.getUserById(id);
+    }
+
+    @Override
+    public Paging<UserVo> getUserPage(UserQuery query) {
+        OrderMapping orderMapping = new OrderMapping();
+        orderMapping.put("createTime", "create_time");
+        PagingUtil.handlePage(query, orderMapping, OrderByItem.desc("id"));
+        List<UserVo> list = userMapper.getUserPage(query);
+        Paging<UserVo> paging = new Paging<>(list);
+        return paging;
+    }
+
+    @Override
+    public AppUserVo getProfile() {
+        Long userId = AppLoginUtil.getUserId();
+        return userMapper.getAppUserById(userId);
+    }
+
+    @Override
+    public boolean updateHead(AppUserHeadDto dto) {
+        Long userId = AppLoginUtil.getUserId();
+        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
+        wrapper.set(User::getHead, dto.getHead());
+        wrapper.eq(User::getId, userId);
+        return update(new User(), wrapper);
+    }
+
+    @Override
+    public boolean updateNickname(AppUserNicknameDto dto) {
+        Long userId = AppLoginUtil.getUserId();
+        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
+        wrapper.set(User::getNickname, dto.getNickname());
+        wrapper.eq(User::getId, userId);
+        return update(new User(), wrapper);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/user/vo/AppUserVo.java b/src/main/java/io/geekidea/boot/user/vo/AppUserVo.java
new file mode 100644
index 00000000..01460465
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/vo/AppUserVo.java
@@ -0,0 +1,48 @@
+package io.geekidea.boot.user.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * App用户信息VO
+ *
+ * @author geekidea
+ * @since 2023-11-25
+ */
+@Data
+@Schema(description = "App用户信息查询结果")
+public class AppUserVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "ID")
+    private Long id;
+
+    @Schema(description = "账号")
+    private String username;
+
+    @Schema(description = "昵称")
+    private String nickname;
+
+    @Schema(description = "手机号码")
+    private String phone;
+
+    @Schema(description = "头像")
+    private String head;
+
+    @Schema(description = "用户角色ID")
+    private Long userRoleId;
+
+    @Schema(description = "用户角色名称")
+    private String userRoleName;
+
+    @Schema(description = "状态 1：正常，0：禁用")
+    private Boolean status;
+
+    @Schema(description = "备注")
+    private String remark;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/user/vo/UserRoleVo.java b/src/main/java/io/geekidea/boot/user/vo/UserRoleVo.java
new file mode 100644
index 00000000..de1094a1
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/vo/UserRoleVo.java
@@ -0,0 +1,46 @@
+package io.geekidea.boot.user.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 用户角色查询结果
+ *
+ * @author geekidea
+ * @since 2024-01-06
+ */
+@Data
+@Schema(description = "用户角色查询结果")
+public class UserRoleVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "主键")
+    private Long id;
+
+    @Schema(description = "用户角色唯一编码")
+    private String code;
+
+    @Schema(description = "用户角色名称")
+    private String name;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "创建人ID")
+    private Long createId;
+
+    @Schema(description = "修改人ID")
+    private Long updateId;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "修改时间")
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/user/vo/UserVo.java b/src/main/java/io/geekidea/boot/user/vo/UserVo.java
new file mode 100644
index 00000000..be3a8c7e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/user/vo/UserVo.java
@@ -0,0 +1,82 @@
+package io.geekidea.boot.user.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 用户信息VO
+ *
+ * @author geekidea
+ * @since 2023-11-30
+ */
+@Data
+@Schema(description = "用户信息查询结果")
+public class UserVo implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "ID")
+    private Long id;
+
+    @Schema(description = "账号")
+    private String username;
+
+    @Schema(description = "昵称")
+    private String nickname;
+
+    @Schema(description = "盐值")
+    private String salt;
+
+    @Schema(description = "微信openid")
+    private String openid;
+
+    @Schema(description = "手机号码")
+    private String phone;
+
+    @Schema(description = "头像")
+    private String head;
+
+    @Schema(description = "用户角色ID")
+    private Long userRoleId;
+
+    @Schema(description = "用户角色编码")
+    private String userRoleCode;
+
+    @Schema(description = "用户角色名称")
+    private String userRoleName;
+
+    @Schema(description = "状态 1：正常，0：禁用")
+    private Boolean status;
+
+    @Schema(description = "注册时间")
+    private Date registerTime;
+
+    @Schema(description = "注册IP")
+    private String registerIp;
+
+    @Schema(description = "注册IP区域")
+    private String registerIpArea;
+
+    @Schema(description = "最后登录时间")
+    private Date lastLoginTime;
+
+    @Schema(description = "最后登录IP")
+    private String lastLoginIp;
+
+    @Schema(description = "最后一次登录IP区域")
+    private String lastLoginIpArea;
+
+    @Schema(description = "备注")
+    private String remark;
+
+    @Schema(description = "创建时间")
+    private Date createTime;
+
+    @Schema(description = "修改时间")
+    private Date updateTime;
+
+}
+
diff --git a/src/main/java/io/geekidea/boot/util/CookieUtil.java b/src/main/java/io/geekidea/boot/util/CookieUtil.java
new file mode 100644
index 00000000..834ad593
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/CookieUtil.java
@@ -0,0 +1,73 @@
+package io.geekidea.boot.util;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author geekidea
+ * @date 2023/12/7
+ **/
+public class CookieUtil {
+
+    /**
+     * 输出token到cookie
+     *
+     * @param name
+     * @param value
+     * @param request
+     * @param response
+     */
+    public static void addCookie(String name, String value, HttpServletRequest request, HttpServletResponse response) {
+        Cookie cookie = new Cookie(name, value);
+        cookie.setPath(request.getContextPath());
+        response.addCookie(cookie);
+    }
+
+    /**
+     * 删除cookie
+     *
+     * @param name
+     * @param request
+     * @param response
+     */
+    public static void deleteCookie(String name, HttpServletRequest request, HttpServletResponse response) {
+        Cookie cookie = new Cookie(name, null);
+        cookie.setMaxAge(0);
+        cookie.setPath(request.getContextPath());
+        response.addCookie(cookie);
+    }
+
+    /**
+     * 通过cookieName从请求中获取cookieValue
+     *
+     * @param request
+     * @param cookieName
+     * @return
+     * @throws Exception
+     */
+    public static String getCookieValueByName(HttpServletRequest request, String cookieName) {
+        Cookie[] cookies = request.getCookies();
+        return getCookieValueByName(cookies, cookieName);
+    }
+
+    /**
+     * 通过cookie名称获取cookie值
+     *
+     * @param cookies
+     * @param cookieName
+     * @return
+     * @throws Exception
+     */
+    public static String getCookieValueByName(Cookie[] cookies, String cookieName) {
+        for (Cookie cookie : cookies) {
+            String name = cookie.getName();
+            if (name.equals(cookieName)) {
+                String value = cookie.getValue();
+                return value;
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/DataRangeUtil.java b/src/main/java/io/geekidea/boot/util/DataRangeUtil.java
new file mode 100644
index 00000000..ce28f899
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/DataRangeUtil.java
@@ -0,0 +1,70 @@
+package io.geekidea.boot.util;
+
+import io.geekidea.boot.auth.util.AppLoginUtil;
+import io.geekidea.boot.auth.util.LoginUtil;
+import io.geekidea.boot.auth.vo.AppLoginVo;
+import io.geekidea.boot.auth.vo.LoginVo;
+import io.geekidea.boot.common.enums.SystemType;
+import io.geekidea.boot.framework.query.DataRangeQuery;
+
+/**
+ * @author geekidea
+ * @date 2023/12/1
+ **/
+public class DataRangeUtil {
+
+    /**
+     * 设置Admin登录用户信息到查询参数中
+     *
+     * @param dataRangeQuery
+     */
+    public static void handleAdminLogin(DataRangeQuery dataRangeQuery) {
+        LoginVo loginVo = LoginUtil.getLoginVo();
+        if (loginVo != null) {
+            Long userId = loginVo.getUserId();
+            dataRangeQuery.setLoginCommonUserId(userId);
+            dataRangeQuery.setLoginAdmin(loginVo.isAdmin());
+            dataRangeQuery.setLoginUserId(userId);
+            dataRangeQuery.setLoginRoleId(loginVo.getRoleId());
+            dataRangeQuery.setLoginRoleCode(loginVo.getRoleCode());
+        }
+    }
+
+    /**
+     * 设置App登录用户信息到查询参数中
+     *
+     * @param dataRangeQuery
+     */
+    public static void handleAppLogin(DataRangeQuery dataRangeQuery) {
+        AppLoginVo appLoginVo = AppLoginUtil.getLoginVo();
+        if (appLoginVo != null) {
+            Long userId = appLoginVo.getUserId();
+            dataRangeQuery.setLoginCommonUserId(userId);
+            dataRangeQuery.setLoginAppUserId(userId);
+            dataRangeQuery.setLoginAppUserRoleId(appLoginVo.getUserRoleId());
+            dataRangeQuery.setLoginAppUserRoleCode(appLoginVo.getUserRoleCode());
+        }
+    }
+
+    /**
+     * 设置登录用户信息到查询参数中
+     *
+     * @param dataRangeQuery
+     */
+    public static void handleCommonLogin(DataRangeQuery dataRangeQuery) {
+        // 判断token是那个端
+        SystemType systemType = SystemTypeUtil.getSystemTypeByToken();
+        if (SystemType.ADMIN == systemType) {
+            LoginVo loginVo = LoginUtil.getLoginVo();
+            if (loginVo != null) {
+                dataRangeQuery.setLoginCommonUserId(loginVo.getUserId());
+            }
+        } else if (SystemType.APP == systemType) {
+            AppLoginVo appLoginVo = AppLoginUtil.getLoginVo();
+            if (appLoginVo != null) {
+                dataRangeQuery.setLoginCommonUserId(appLoginVo.getUserId());
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/DateDiffUtil.java b/src/main/java/io/geekidea/boot/util/DateDiffUtil.java
new file mode 100644
index 00000000..4ef43e89
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/DateDiffUtil.java
@@ -0,0 +1,31 @@
+package io.geekidea.boot.util;
+
+/**
+ * @author geekidea
+ * @date 2023/12/16
+ **/
+public class DateDiffUtil {
+
+    /**
+     * 计算相差的时间天到秒
+     * xx天xx小时xx分钟xx秒
+     *
+     * @param diffTime
+     * @return
+     */
+    public static String getDiffDaySecond(long diffTime) {
+        // 计算相差的秒数
+        long diff = diffTime / 1000;
+        // 计算天
+        long day = diff / (24 * 60 * 60);
+        // 计算小时数
+        long hour = diff / (60 * 60);
+        // 计算分钟数
+        long minute = (diff % (60 * 60)) / 60;
+        // 计算秒数
+        long second = diff % 60;
+        String desc = day + "天" + hour + "小时" + minute + "分钟" + second + "秒";
+        return desc;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/ExcelExportUtil.java b/src/main/java/io/geekidea/boot/util/ExcelExportUtil.java
new file mode 100644
index 00000000..e0f9cf8e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/ExcelExportUtil.java
@@ -0,0 +1,78 @@
+package io.geekidea.boot.util;
+
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.converters.longconverter.LongStringConverter;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.style.WriteCellStyle;
+import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Excel导出工具类
+ *
+ * @author geekidea
+ * @date 2022/8/22
+ **/
+public class ExcelExportUtil {
+
+    private static final String CONTENT_TYPE = "application/vnd.ms-excel";
+    private static final String CONTENT_DISPOSITION = "Content-disposition";
+    private static final String CHARACTER_ENCODING = "UTF-8";
+    private static final String EXCEL_SUFFIX = ".xlsx";
+
+
+    public static void export(List<?> list, Class<?> headClass, HttpServletResponse response) {
+        export(list, headClass, null, null, response);
+    }
+
+    public static void export(List<?> list, Class<?> headClass, String exportFileName, HttpServletResponse response) {
+        export(list, headClass, exportFileName, null, response);
+    }
+
+    public static void export(List<?> list, Class<?> headClass, String exportFileName, String sheetName, HttpServletResponse response) {
+        response.setContentType(CONTENT_TYPE);
+        response.setCharacterEncoding(CHARACTER_ENCODING);
+        ExcelWriter excelWriter = null;
+        try {
+            WriteCellStyle headWriteCellStyle = new WriteCellStyle();
+            headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
+            WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
+            //设置内容水平居中
+            contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+            HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
+
+            if (StringUtils.isBlank(exportFileName)) {
+                String currentTime = new SimpleDateFormat("yyyyMMddHHmmssS").format(new Date());
+                String random = RandomStringUtils.randomNumeric(6);
+                exportFileName = currentTime + random;
+            }
+            exportFileName = URLEncoder.encode(exportFileName, CHARACTER_ENCODING);
+            response.setHeader(CONTENT_DISPOSITION, "attachment;filename=" + exportFileName + EXCEL_SUFFIX);
+            excelWriter = EasyExcel.write(response.getOutputStream())
+                    .registerWriteHandler(horizontalCellStyleStrategy)
+                    .registerConverter(new LongStringConverter())
+                    .build();
+            WriteSheet mainSheet = EasyExcel.writerSheet(0, sheetName).head(headClass).build();
+            excelWriter.write(list, mainSheet);
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (excelWriter != null) {
+                excelWriter.finish();
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/HttpServletRequestUtil.java b/src/main/java/io/geekidea/boot/util/HttpServletRequestUtil.java
new file mode 100644
index 00000000..10f6c8bf
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/HttpServletRequestUtil.java
@@ -0,0 +1,55 @@
+package io.geekidea.boot.util;
+
+import io.geekidea.boot.common.constant.CommonConstant;
+import io.geekidea.boot.common.constant.RequestHeadConstant;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 获取当前请求的HttpServletRequest对象
+ *
+ * @author geekidea
+ * @date 2018-11-08
+ */
+public class HttpServletRequestUtil {
+
+    /**
+     * 获取HttpServletRequest对象
+     *
+     * @return
+     */
+    public static HttpServletRequest getRequest() {
+        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+    }
+
+    /**
+     * 判断是否是Knife4j或是Swagger接口文档访问的请求
+     *
+     * @return
+     */
+    public static boolean isDocRequest() {
+        return isDocRequest(getRequest());
+    }
+
+    /**
+     * 判断是否是Knife4j或是Swagger接口文档访问的请求
+     *
+     * @param request
+     * @return
+     */
+    public static boolean isDocRequest(HttpServletRequest request) {
+        String requestOrigion = request.getHeader(RequestHeadConstant.REQUEST_ORIGION);
+        String referer = request.getHeader(RequestHeadConstant.REFERER);
+        boolean docRequest = false;
+        if (CommonConstant.KNIFE4J.equals(requestOrigion)) {
+            docRequest = true;
+        } else if (StringUtils.isNotBlank(referer) && referer.contains(CommonConstant.SWAGGER_UI_PATH)) {
+            docRequest = true;
+        }
+        return docRequest;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/HttpServletResponseUtil.java b/src/main/java/io/geekidea/boot/util/HttpServletResponseUtil.java
new file mode 100644
index 00000000..c603ba0a
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/HttpServletResponseUtil.java
@@ -0,0 +1,63 @@
+package io.geekidea.boot.util;
+
+import com.alibaba.fastjson2.JSON;
+import io.geekidea.boot.framework.response.ApiResult;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author geekidea
+ * @date 2018-11-08
+ */
+public final class HttpServletResponseUtil {
+
+    private static String UTF8 = "UTF-8";
+    private static String CONTENT_TYPE = "application/json";
+
+    private HttpServletResponseUtil() {
+        throw new AssertionError();
+    }
+
+
+    public static void printFailJsonMessage(HttpServletResponse response, String errorMessage) {
+        ApiResult apiResult = ApiResult.fail(errorMessage);
+        printJson(response, apiResult);
+    }
+
+    public static void printJson(HttpServletResponse response, Object object) {
+        response.setCharacterEncoding(UTF8);
+        response.setContentType(CONTENT_TYPE);
+        PrintWriter printWriter = null;
+        try {
+            printWriter = response.getWriter();
+            printWriter.write(JSON.toJSONString(object));
+            printWriter.flush();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (printWriter != null) {
+                printWriter.close();
+            }
+        }
+    }
+
+    public static void print(HttpServletResponse response, String string) {
+        response.setCharacterEncoding(UTF8);
+        response.setContentType(CONTENT_TYPE);
+        PrintWriter printWriter = null;
+        try {
+            printWriter = response.getWriter();
+            printWriter.write(string);
+            printWriter.flush();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (printWriter != null) {
+                printWriter.close();
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/IdUtil.java b/src/main/java/io/geekidea/boot/util/IdUtil.java
new file mode 100644
index 00000000..472df0d9
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/IdUtil.java
@@ -0,0 +1,20 @@
+package io.geekidea.boot.util;
+
+import com.github.yitter.idgen.YitIdHelper;
+
+/**
+ * @author geekidea
+ * @date 2024/2/12
+ **/
+public class IdUtil {
+
+    /**
+     * 获取短的雪花算法ID
+     *
+     * @return
+     */
+    public static long getId() {
+        return YitIdHelper.nextId();
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/IpRegionUtil.java b/src/main/java/io/geekidea/boot/util/IpRegionUtil.java
new file mode 100644
index 00000000..296f95d4
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/IpRegionUtil.java
@@ -0,0 +1,145 @@
+package io.geekidea.boot.util;
+
+import io.geekidea.boot.framework.bean.IpRegion;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.lionsoul.ip2region.xdb.Searcher;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.stereotype.Component;
+import org.springframework.util.FileCopyUtils;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * IP归属地信息工具类
+ *
+ * @author geekidea
+ * @date 2023/6/23
+ **/
+@Slf4j
+@Component
+public class IpRegionUtil {
+
+    private static final String CHINA = "中国";
+
+    private static final String PROVINCE = "省";
+
+    private static final String CITY = "市";
+
+    private static final String INTRANET = "内网IP";
+
+    private static Searcher searcher;
+
+    /**
+     * 程序启动时，将ip2region.xdb一次性加载到内存中
+     * 并发场景下可安全使用
+     */
+    @PostConstruct
+    private static void init() {
+        InputStream inputStream = null;
+        try {
+            inputStream = new ClassPathResource("/ip2region.xdb").getInputStream();
+            byte[] buff = FileCopyUtils.copyToByteArray(inputStream);
+            searcher = Searcher.newWithBuffer(buff);
+            log.info("加载ip2region.xdb成功");
+        } catch (IOException e) {
+            log.error("加载ip2region.xdb异常");
+            e.printStackTrace();
+        } finally {
+            try {
+                if (inputStream != null) {
+                    inputStream.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * 根据IP获取区域信息
+     *
+     * @param ip
+     * @return
+     */
+    public static IpRegion getIpRegion(String ip) {
+        try {
+            String region = searcher.search(ip);
+            if (StringUtils.isBlank(region)) {
+                return null;
+            }
+            String[] array = region.split("\\|");
+            String country = array[0];
+            String province = null;
+            String city = null;
+            String isp = null;
+            String areaDesc;
+            if (CHINA.equals(country)) {
+                province = array[2];
+                city = array[3];
+                isp = array[4];
+                if (province.endsWith(PROVINCE)) {
+                    province = province.substring(0, province.length() - 1);
+                }
+                if (city.endsWith(CITY)) {
+                    city = city.substring(0, city.length() - 1);
+                }
+                if (province.equals(city)) {
+                    areaDesc = province;
+                } else {
+                    areaDesc = province + " " + city;
+                }
+            } else if (region.contains(INTRANET)) {
+                areaDesc = INTRANET;
+            } else {
+                province = array[2];
+                isp = array[4];
+                if (StringUtils.isBlank(province)) {
+                    areaDesc = country;
+                } else {
+                    areaDesc = country + " " + province;
+                }
+            }
+            IpRegion ipRegion = new IpRegion();
+            ipRegion.setCountry(country);
+            ipRegion.setProvince(province);
+            ipRegion.setCity(city);
+            ipRegion.setAreaDesc(areaDesc);
+            ipRegion.setIsp(isp);
+            return ipRegion;
+        } catch (Exception e) {
+            log.error("解析IP归属地信息异常：" + ip);
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 通过IP获取区域描述
+     *
+     * @param ip
+     * @return
+     */
+    public static String getIpAreaDesc(String ip) {
+        IpRegion ipRegion = getIpRegion(ip);
+        if (ipRegion != null) {
+            return ipRegion.getAreaDesc();
+        }
+        return null;
+    }
+
+    @PreDestroy
+    public static void destroy() {
+        try {
+            if (searcher != null) {
+                searcher.close();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/IpUtil.java b/src/main/java/io/geekidea/boot/util/IpUtil.java
new file mode 100644
index 00000000..89697cf5
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/IpUtil.java
@@ -0,0 +1,152 @@
+package io.geekidea.boot.util;
+
+/**
+ * @author geekidea
+ * @date 2022/7/31
+ **/
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.regex.Pattern;
+
+
+@Slf4j
+public class IpUtil {
+    private static final String UNKNOWN = "unknown";
+    private static final String IPV6_LOCAL = "0:0:0:0:0:0:0:1";
+    private static final String COMMA = ",";
+    private static final String FE = "fe";
+    private static final String[] IP_HEADS = new String[]{"x-forwarded-for", "X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP"};
+
+    /**
+     * 获取请求用户的IP地址
+     *
+     * @return
+     */
+    public static String getRequestIp() {
+        HttpServletRequest request = HttpServletRequestUtil.getRequest();
+        String ip = getRequestIp(request);
+        if (ip.indexOf(COMMA) != -1) {
+            String[] strings = ip.split(COMMA);
+            ip = strings[0];
+        }
+        return ip;
+    }
+
+    /**
+     * 获取请求用户的IP地址
+     *
+     * @param request
+     * @return
+     */
+    public static String getRequestIp(HttpServletRequest request) {
+        String ip = null;
+        for (String ipHead : IP_HEADS) {
+            ip = request.getHeader(ipHead);
+            if (StringUtils.isNotBlank(ip) && !UNKNOWN.equalsIgnoreCase(ip)) {
+                break;
+            }
+        }
+        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+        if (IPV6_LOCAL.equals(ip)) {
+            ip = getLocalhostIp();
+        }
+        return ip;
+    }
+
+    public static String getLocalhostIp() {
+        try {
+            return InetAddress.getLocalHost().getHostAddress();
+        } catch (UnknownHostException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static List<String> getLocalhostIpList() {
+        List<String> list = new ArrayList<>();
+        try {
+            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+            if (ObjectUtils.isEmpty(networkInterfaces)) {
+                return null;
+            }
+            while (networkInterfaces.hasMoreElements()) {
+                NetworkInterface networkInterface = networkInterfaces.nextElement();
+                if (!isValidateNetworkInterface(networkInterface)) {
+                    continue;
+                }
+                Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
+                while (inetAddresses.hasMoreElements()) {
+                    InetAddress inetAddress = inetAddresses.nextElement();
+                    if (!isValidateAddress(inetAddress)) {
+                        continue;
+                    }
+                    list.add(inetAddress.getHostAddress());
+                }
+            }
+        } catch (SocketException e) {
+            e.printStackTrace();
+        }
+        return list;
+    }
+
+    /**
+     * 是否是有效的网络
+     *
+     * @param networkInterface
+     * @return
+     */
+    private static boolean isValidateNetworkInterface(NetworkInterface networkInterface) {
+        try {
+            return !networkInterface.isLoopback() && !networkInterface.isPointToPoint() && networkInterface.isUp() && !networkInterface.isVirtual();
+        } catch (SocketException e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    /**
+     * 是否是有效的IP
+     *
+     * @param inetAddress
+     * @return
+     */
+    private static boolean isValidateAddress(InetAddress inetAddress) {
+        try {
+            String hostAddress = inetAddress.getHostAddress();
+            // 排除IPV6地址
+            if (hostAddress.startsWith(FE)) {
+                return false;
+            }
+            if (!isValidIpV4(hostAddress)) {
+                return false;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return true;
+    }
+
+    /**
+     * 校验是否是有效的IPV4
+     *
+     * @param ipAddress
+     * @return
+     */
+    public static boolean isValidIpV4(String ipAddress) {
+        return Pattern.matches("^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", ipAddress);
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/io/geekidea/boot/util/ObjectValueUtil.java b/src/main/java/io/geekidea/boot/util/ObjectValueUtil.java
new file mode 100644
index 00000000..b713c737
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/ObjectValueUtil.java
@@ -0,0 +1,65 @@
+package io.geekidea.boot.util;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.reflect.Field;
+
+/**
+ * 判断对象中的属性是否有值
+ *
+ * @author geekidea
+ * @date 2023/6/20
+ **/
+public class ObjectValueUtil {
+
+    private static String SERIAL_VERSION_UID = "serialVersionUID";
+
+    /**
+     * 对象的属性是否没有值
+     *
+     * @param object
+     * @return
+     */
+    public static boolean isNotHaveValue(Object object) {
+        return !isHaveValue(object);
+    }
+
+    /**
+     * 对象的属性是否有值
+     *
+     * @param object
+     * @return
+     */
+    public static boolean isHaveValue(Object object) {
+        if (object == null) {
+            return false;
+        }
+        Class<?> clazz = object.getClass();
+        Field[] fields = clazz.getDeclaredFields();
+        for (Field field : fields) {
+            field.setAccessible(true);
+            String name = field.getName();
+            if (SERIAL_VERSION_UID.equals(name)) {
+                continue;
+            }
+            try {
+                Object value = field.get(object);
+                Class<?> type = field.getType();
+                if (type == String.class) {
+                    String string = (String) value;
+                    if (StringUtils.isNotBlank(string)) {
+                        return true;
+                    }
+                } else {
+                    if (value != null) {
+                        return true;
+                    }
+                }
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/PagingUtil.java b/src/main/java/io/geekidea/boot/util/PagingUtil.java
new file mode 100644
index 00000000..f7b494b2
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/PagingUtil.java
@@ -0,0 +1,65 @@
+package io.geekidea.boot.util;
+
+import com.github.pagehelper.PageHelper;
+import io.geekidea.boot.common.constant.CommonConstant;
+import io.geekidea.boot.framework.page.BasePageQuery;
+import io.geekidea.boot.framework.page.OrderByItem;
+import io.geekidea.boot.framework.page.OrderMapping;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @author geekidea
+ * @date 2023/11/19
+ **/
+public class PagingUtil {
+
+    /**
+     * 处理分页
+     *
+     * @param basePageQuery
+     */
+    public static void handlePage(BasePageQuery basePageQuery) {
+        handlePage(basePageQuery, null);
+    }
+
+
+    /**
+     * 处理分页
+     *
+     * @param basePageQuery
+     * @param defaultOrderBy
+     */
+    public static void handlePage(BasePageQuery basePageQuery, String defaultOrderBy) {
+        handlePage(basePageQuery, null, defaultOrderBy);
+    }
+
+    public static void handlePage(BasePageQuery basePageQuery, OrderMapping orderMapping, String defaultOrderBy) {
+        String orderBy = defaultOrderBy;
+        Integer pageIndex = CommonConstant.DEFAULT_PAGE_INDEX;
+        Integer pageSize = CommonConstant.DEFAULT_PAGE_SIZE;
+        if (basePageQuery != null) {
+            pageIndex = basePageQuery.getPageIndex();
+            pageSize = basePageQuery.getPageSize();
+            // 判断参数中是否有排序
+            String orderByColumn = basePageQuery.getOrderByColumn();
+            if (StringUtils.isNotBlank(orderByColumn)) {
+                // 获取列映射
+                if (orderMapping != null && orderMapping.size() > 0) {
+                    String dbColumn = orderMapping.get(orderByColumn);
+                    // 如果没找到对应的数据库映射列，则使用当前参数列名称
+                    if (StringUtils.isNotBlank(dbColumn)) {
+                        orderByColumn = dbColumn;
+                    }
+                }
+                Boolean orderByAsc = basePageQuery.getOrderByAsc();
+                String paramOrderBy = OrderByItem.orderBy(orderByColumn, orderByAsc);
+                if (StringUtils.isNotBlank(paramOrderBy)) {
+                    orderBy = paramOrderBy;
+                }
+            }
+        }
+        // 分页
+        PageHelper.startPage(pageIndex, pageSize, orderBy);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/PasswordUtil.java b/src/main/java/io/geekidea/boot/util/PasswordUtil.java
new file mode 100644
index 00000000..5d175297
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/PasswordUtil.java
@@ -0,0 +1,30 @@
+package io.geekidea.boot.util;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @author geekidea
+ * @date 2022/7/13
+ **/
+public class PasswordUtil {
+
+    private static int MD5_PWD_LENGTH = 32;
+
+    public static String encrypt(String password, String salt) {
+        if (StringUtils.isBlank(password)) {
+            throw new IllegalArgumentException("密码不能为空");
+        }
+        if (StringUtils.isBlank(salt)) {
+            salt = "";
+        }
+        // 如果密码长度不是32为，则进行md5加密
+        if (password.length() != MD5_PWD_LENGTH) {
+            password = DigestUtils.md5Hex(password);
+        }
+        // 将md5加密后的结果+盐，再进行sha256加密
+        String encryptPassword = DigestUtils.sha256Hex(password + salt);
+        return encryptPassword;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/ServerInfoUtil.java b/src/main/java/io/geekidea/boot/util/ServerInfoUtil.java
new file mode 100644
index 00000000..7b20c52e
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/ServerInfoUtil.java
@@ -0,0 +1,258 @@
+package io.geekidea.boot.util;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.NumberUtil;
+import cn.hutool.system.*;
+import cn.hutool.system.oshi.CpuInfo;
+import cn.hutool.system.oshi.OshiUtil;
+import io.geekidea.boot.framework.bean.*;
+import oshi.SystemInfo;
+import oshi.hardware.GlobalMemory;
+import oshi.software.os.FileSystem;
+import oshi.software.os.OSFileStore;
+import oshi.software.os.OSProcess;
+import oshi.software.os.OperatingSystem;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 获取系统服务信息
+ *
+ * @author geekidea
+ * @date 2023/12/16
+ **/
+public class ServerInfoUtil {
+
+    /**
+     * 获取服务器信息
+     *
+     * @return
+     */
+    public static ServerInfo getServerInfo() {
+        Cpu cpu = getCpu();
+        Memory memory = getMemory();
+        Jvm jvm = getJvm();
+        List<Disk> diskList = getDiskList();
+        OperatingSystemInfo operatingSystemInfo = getOperatingSystemInfo();
+        ServerInfo serverInfo = new ServerInfo();
+        serverInfo.setCpu(cpu);
+        serverInfo.setMemory(memory);
+        serverInfo.setJvm(jvm);
+        serverInfo.setDiskList(diskList);
+        serverInfo.setOperatingSystemInfo(operatingSystemInfo);
+        return serverInfo;
+    }
+
+    /**
+     * 获取CPU信息
+     *
+     * @return
+     */
+    public static Cpu getCpu() {
+        try {
+            CpuInfo cpuInfo = OshiUtil.getCpuInfo();
+            // CPU核心数
+            Integer cpuNum = cpuInfo.getCpuNum();
+            // 总CPU使用率：获取用户+系统的总的CPU使用率
+            double used = cpuInfo.getUsed();
+            // CPU系统使用率
+            double sys = cpuInfo.getSys();
+            // CPU用户使用率
+            double user = cpuInfo.getUser();
+            // CPU当前空闲率
+            double free = cpuInfo.getFree();
+            Cpu cpu = new Cpu();
+            cpu.setCpuNum(cpuNum);
+            cpu.setSys(sys);
+            cpu.setUser(user);
+            cpu.setFree(free);
+            cpu.setUsed(used);
+            return cpu;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 获取内存信息
+     *
+     * @return
+     */
+    public static Memory getMemory() {
+        try {
+            GlobalMemory globalMemory = OshiUtil.getMemory();
+            // 总内存
+            long total = globalMemory.getTotal();
+            // 空闲内存
+            long available = globalMemory.getAvailable();
+            // 已使用的
+            long used = total - available;
+            String totalUnit = FileUtil.readableFileSize(total);
+            String availableUnit = FileUtil.readableFileSize(available);
+            String usedUnit = FileUtil.readableFileSize(used);
+            BigDecimal availableRate = NumberUtil.round(available * 1.0 / total * 100, 2);
+            BigDecimal usedRate = NumberUtil.sub(100, availableRate);
+            Memory memory = new Memory();
+            memory.setTotal(total);
+            memory.setAvailable(available);
+            memory.setUsed(used);
+            memory.setTotalUnit(totalUnit);
+            memory.setAvailableUnit(availableUnit);
+            memory.setUsedUnit(usedUnit);
+            memory.setAvailableRate(availableRate);
+            memory.setUsedRate(usedRate);
+            return memory;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 获取JVM信息
+     *
+     * @return
+     */
+    public static Jvm getJvm() {
+        try {
+            RuntimeInfo runtimeInfo = SystemUtil.getRuntimeInfo();
+            // JVM已分配内存
+            long totalMemory = runtimeInfo.getTotalMemory();
+            // JVM最大内存
+            long maxMemory = runtimeInfo.getMaxMemory();
+            // JVM最大可用内存
+            long usableMemory = runtimeInfo.getUsableMemory();
+            // JVM已分配内存中的剩余空间
+            long freeMemory = runtimeInfo.getFreeMemory();
+            String totalMemoryUnit = FileUtil.readableFileSize(totalMemory);
+            String maxMemoryUnit = FileUtil.readableFileSize(maxMemory);
+            String usableMemoryUnit = FileUtil.readableFileSize(usableMemory);
+            String freeMemoryUnit = FileUtil.readableFileSize(freeMemory);
+            JvmInfo jvmInfo = SystemUtil.getJvmInfo();
+            // JVM名称
+            String name = jvmInfo.getName();
+            // JVM厂商
+            String vendor = jvmInfo.getVendor();
+            JavaInfo javaInfo = SystemUtil.getJavaInfo();
+            // java版本
+            String version = javaInfo.getVersion();
+            // java按照路径
+            String home = SystemUtil.get("java.home");
+            OSProcess currentProcess = OshiUtil.getCurrentProcess();
+            // 当前进程ID
+            int processID = currentProcess.getProcessID();
+            // 当前进程启动时间
+            long startTime = currentProcess.getStartTime();
+            // 当前进程已使用时间
+            long userTime = currentProcess.getUserTime();
+            // 当前进程已使用时间描述
+            String userTimeDesc = DateDiffUtil.getDiffDaySecond(userTime);
+            // 当前进程Cpu使用率
+            double processCpuLoadCumulative = currentProcess.getProcessCpuLoadCumulative();
+            BigDecimal processCpuRate = NumberUtil.round(processCpuLoadCumulative * 100, 2);
+            Jvm jvm = new Jvm();
+            jvm.setTotalMemory(totalMemory);
+            jvm.setMaxMemory(maxMemory);
+            jvm.setUsableMemory(usableMemory);
+            jvm.setFreeMemory(freeMemory);
+            jvm.setTotalMemoryUnit(totalMemoryUnit);
+            jvm.setMaxMemoryUnit(maxMemoryUnit);
+            jvm.setUsableMemoryUnit(usableMemoryUnit);
+            jvm.setFreeMemoryUnit(freeMemoryUnit);
+            jvm.setName(name);
+            jvm.setVendor(vendor);
+            jvm.setVersion(version);
+            jvm.setHome(home);
+            jvm.setProcessId(processID);
+            jvm.setStartTime(new Date(startTime));
+            jvm.setUserTime(userTime);
+            jvm.setUserTimeDesc(userTimeDesc);
+            jvm.setProcessCpuRate(processCpuRate);
+            return jvm;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 获取磁盘列表
+     *
+     * @return
+     */
+    public static List<Disk> getDiskList() {
+        try {
+            SystemInfo systemInfo = new SystemInfo();
+            OperatingSystem operatingSystem = systemInfo.getOperatingSystem();
+            FileSystem fileSystem = operatingSystem.getFileSystem();
+            List<OSFileStore> fileStores = fileSystem.getFileStores();
+            List<Disk> disks = new ArrayList<>();
+            for (OSFileStore fileStore : fileStores) {
+                // 磁盘路径
+                String mount = fileStore.getMount();
+                // 文件系统类型
+                String type = fileStore.getType();
+                // 磁盘名称
+                String name = fileStore.getName();
+                // 磁盘总大小
+                long totalSpace = fileStore.getTotalSpace();
+                // 磁盘可用空间大小
+                long usableSpace = fileStore.getUsableSpace();
+                // 磁盘已使用空间大小
+                long usedSpace = totalSpace - usableSpace;
+                String totalSpaceUnit = FileUtil.readableFileSize(totalSpace);
+                String usableSpaceUnit = FileUtil.readableFileSize(usableSpace);
+                String usedSpaceUnit = FileUtil.readableFileSize(usedSpace);
+                BigDecimal usableRate = NumberUtil.round(usableSpace * 1.0 / totalSpace * 100, 2);
+                BigDecimal usedRate = NumberUtil.sub(100.0, usableRate);
+                Disk disk = new Disk();
+                disk.setMount(mount);
+                disk.setType(type);
+                disk.setName(name);
+                disk.setTotalSpace(totalSpace);
+                disk.setUsableSpace(usableSpace);
+                disk.setUsedSpace(usedSpace);
+                disk.setTotalSpaceUnit(totalSpaceUnit);
+                disk.setUsableSpaceUnit(usableSpaceUnit);
+                disk.setUsedSpaceSpaceUnit(usedSpaceUnit);
+                disk.setUsableRate(usableRate);
+                disk.setUsedRate(usedRate);
+                disks.add(disk);
+            }
+            return disks;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 获取操作系统信息
+     *
+     * @return
+     */
+    public static OperatingSystemInfo getOperatingSystemInfo() {
+        try {
+            OsInfo osInfo = SystemUtil.getOsInfo();
+            String name = osInfo.getName();
+            String arch = osInfo.getArch();
+            String version = osInfo.getVersion();
+            HostInfo hostInfo = SystemUtil.getHostInfo();
+            String address = hostInfo.getAddress();
+            OperatingSystemInfo operatingSystemInfo = new OperatingSystemInfo();
+            operatingSystemInfo.setName(name);
+            operatingSystemInfo.setArch(arch);
+            operatingSystemInfo.setVersion(version);
+            operatingSystemInfo.setAddress(address);
+            return operatingSystemInfo;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/SystemTypeUtil.java b/src/main/java/io/geekidea/boot/util/SystemTypeUtil.java
new file mode 100644
index 00000000..8c9e8195
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/SystemTypeUtil.java
@@ -0,0 +1,119 @@
+package io.geekidea.boot.util;
+
+import io.geekidea.boot.auth.util.TokenUtil;
+import io.geekidea.boot.common.constant.LoginConstant;
+import io.geekidea.boot.common.enums.SystemType;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author geekidea
+ * @date 2023/11/22
+ **/
+public class SystemTypeUtil {
+
+    /**
+     * 获取系统类型枚举
+     *
+     * @param request
+     * @return
+     * @throws Exception
+     */
+    public static SystemType getSystemTypeByPath(HttpServletRequest request) {
+        String servletPath = request.getServletPath();
+        return getSystemTypeByPath(servletPath);
+    }
+
+    /**
+     * 根据请求路径获取系统类型枚举
+     *
+     * @param servletPath
+     * @return
+     * @throws Exception
+     */
+    public static SystemType getSystemTypeByPath(String servletPath) {
+        if (StringUtils.isBlank(servletPath)) {
+            return null;
+        }
+        if (servletPath.startsWith(LoginConstant.ADMIN_URL_PREFIX)) {
+            return SystemType.ADMIN;
+        } else if (servletPath.startsWith(LoginConstant.APP_URL_PREFIX)) {
+            return SystemType.APP;
+        }
+        return null;
+    }
+
+    /**
+     * 判断token是那个端
+     *
+     * @param token
+     * @return
+     * @throws Exception
+     */
+    public static SystemType getSystemTypeByToken(String token) {
+        if (StringUtils.isBlank(token)) {
+            return null;
+        }
+        if (token.startsWith(LoginConstant.ADMIN_TOKEN_PREFIX)) {
+            return SystemType.ADMIN;
+        } else if (token.startsWith(LoginConstant.APP_TOKEN_PREFIX)) {
+            return SystemType.APP;
+        }
+        return null;
+    }
+
+    /**
+     * 判断token是那个端
+     *
+     * @return
+     * @throws Exception
+     */
+    public static SystemType getSystemTypeByToken() {
+        String token = TokenUtil.getToken();
+        return getSystemTypeByToken(token);
+    }
+
+    /**
+     * 判断token是那个端
+     *
+     * @return
+     * @throws Exception
+     */
+    public static Integer getSystemTypeCodeByToken() {
+        SystemType systemType = getSystemTypeByToken();
+        if (systemType != null) {
+            return systemType.getCode();
+        }
+        return null;
+    }
+
+    /**
+     * 判断是否是管理后台系统
+     *
+     * @return
+     * @throws Exception
+     */
+    public static boolean isAdminSystem() {
+        SystemType systemType = getSystemTypeByToken();
+        if (SystemType.ADMIN == systemType) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 判断是否是APP移动端系统
+     *
+     * @return
+     * @throws Exception
+     */
+    public static boolean isAppSystem() {
+        SystemType systemType = getSystemTypeByToken();
+        if (SystemType.APP == systemType) {
+            return true;
+        }
+        return false;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/TraceIdUtil.java b/src/main/java/io/geekidea/boot/util/TraceIdUtil.java
new file mode 100644
index 00000000..15c2e3bd
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/TraceIdUtil.java
@@ -0,0 +1,21 @@
+package io.geekidea.boot.util;
+
+import io.geekidea.boot.common.constant.CommonConstant;
+import org.slf4j.MDC;
+
+/**
+ * @author geekidea
+ * @date 2023/11/26
+ **/
+public class TraceIdUtil {
+
+    /**
+     * 获取当前请求日志链路ID
+     *
+     * @return
+     */
+    public static String getTraceId() {
+        return MDC.get(CommonConstant.TRACE_ID);
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/UUIDUtil.java b/src/main/java/io/geekidea/boot/util/UUIDUtil.java
new file mode 100644
index 00000000..727c45dd
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/UUIDUtil.java
@@ -0,0 +1,14 @@
+package io.geekidea.boot.util;
+
+import java.util.UUID;
+
+/**
+ * @author geekidea
+ * @date 2022/6/26
+ **/
+public class UUIDUtil {
+
+    public static String getUuid() {
+        return UUID.randomUUID().toString().replaceAll("-", "");
+    }
+}
diff --git a/src/main/java/io/geekidea/boot/util/YamlUtil.java b/src/main/java/io/geekidea/boot/util/YamlUtil.java
new file mode 100644
index 00000000..90608cef
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/YamlUtil.java
@@ -0,0 +1,42 @@
+package io.geekidea.boot.util;
+
+import io.geekidea.boot.common.constant.CommonConstant;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2023/11/17
+ **/
+public class YamlUtil {
+
+    /**
+     * 解析配置文件总的list行数据为数组
+     *
+     * @param list
+     * @return
+     */
+    public static List<String> parseListArray(List<String> list) {
+        if (CollectionUtils.isEmpty(list)) {
+            return null;
+        }
+        List<String> parseList = new ArrayList<>();
+        for (String string : list) {
+            if (StringUtils.isBlank(string)) {
+                continue;
+            }
+            if (string.contains(CommonConstant.COMMA)) {
+                String[] array = string.split(CommonConstant.COMMA);
+                parseList.addAll(Arrays.asList(array));
+            } else {
+                parseList.add(string);
+            }
+        }
+        return parseList;
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/api/OssApi.java b/src/main/java/io/geekidea/boot/util/api/OssApi.java
new file mode 100644
index 00000000..497f12ca
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/api/OssApi.java
@@ -0,0 +1,94 @@
+package io.geekidea.boot.util.api;
+
+import com.alibaba.fastjson2.JSON;
+import com.aliyun.oss.ClientException;
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.OSSException;
+import com.aliyun.oss.common.auth.CredentialsProviderFactory;
+import com.aliyun.oss.common.auth.DefaultCredentialProvider;
+import com.aliyun.oss.model.PutObjectRequest;
+import com.aliyun.oss.model.PutObjectResult;
+import io.geekidea.boot.config.properties.OssProperties;
+import io.geekidea.boot.framework.exception.BusinessException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import java.io.InputStream;
+
+/**
+ * OSS接口调用工具类
+ *
+ * @author geekidea
+ * @date 2023/11/26
+ **/
+@Slf4j
+@Component
+@ConditionalOnProperty(name = "file.file-server-type", havingValue = "OSS", matchIfMissing = true)
+public class OssApi {
+
+    private static OssProperties ossProperties;
+
+    private static OSS ossClient;
+
+    public OssApi(OssProperties ossProperties) {
+        OssApi.ossProperties = ossProperties;
+    }
+
+    @PostConstruct
+    public void initOssClient() {
+        try {
+            DefaultCredentialProvider credentialsProvider = CredentialsProviderFactory.newDefaultCredentialProvider(ossProperties.getAccessKeyId(), ossProperties.getAccessKeySecret());
+            // 创建OSSClient实例
+            ossClient = new OSSClientBuilder().build(ossProperties.getEndpoint(), credentialsProvider);
+            log.info("OSS实例初始化成功：" + JSON.toJSONString(ossProperties));
+        } catch (Exception e) {
+            log.error("OSS实例初始化异常：" + JSON.toJSONString(ossProperties));
+            e.printStackTrace();
+        }
+    }
+
+    public static String upload(InputStream inputStream, String dirName, String fileName) {
+        try {
+            String rootDir = ossProperties.getRootDir();
+            String fileKey = "";
+            if (StringUtils.isNotBlank(rootDir)) {
+                fileKey = rootDir + "/";
+            }
+            fileKey = fileKey + dirName + "/" + fileName;
+            log.info("OSS上传文件fileKey：" + fileKey);
+            PutObjectRequest putObjectRequest = new PutObjectRequest(ossProperties.getBucketName(), fileKey, inputStream);
+            PutObjectResult result = ossClient.putObject(putObjectRequest);
+            log.info("OSS上传文件结果：" + JSON.toJSONString(result));
+            String accessUrl = ossProperties.getAccessDomain();
+            String url = accessUrl + "/" + fileKey;
+            // 返回访问路径
+            log.info("OSS上传文件成功，fileKey：{}，url：{}", fileKey, url);
+            return url;
+        } catch (OSSException oe) {
+            log.error("OSS上传文件异常，错误消息：{}，错误码：{}，请求ID：{}，主机ID：{}", oe.getErrorMessage(), oe.getErrorCode(), oe.getRequestId(), oe.getHostId());
+            throw new BusinessException("OSS上传文件异常");
+        } catch (ClientException ce) {
+            log.error("OSS客户端异常：" + ce.getMessage());
+            throw new BusinessException("OSS连接异常");
+        } catch (Exception e) {
+            throw e;
+        }
+    }
+
+    @PreDestroy
+    public void destroy() {
+        try {
+            if (ossClient != null) {
+                ossClient.shutdown();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}
diff --git a/src/main/java/io/geekidea/boot/util/api/WxMpApi.java b/src/main/java/io/geekidea/boot/util/api/WxMpApi.java
new file mode 100644
index 00000000..3245156c
--- /dev/null
+++ b/src/main/java/io/geekidea/boot/util/api/WxMpApi.java
@@ -0,0 +1,60 @@
+package io.geekidea.boot.util.api;
+
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson2.JSONObject;
+import io.geekidea.boot.config.properties.WxMpProperties;
+import io.geekidea.boot.framework.exception.BusinessException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 微信小程序接口调用工具类
+ *
+ * @author geekidea
+ * @date 2023/11/26
+ **/
+@Slf4j
+@Component
+public class WxMpApi {
+
+    private static WxMpProperties wxMpProperties;
+
+    public WxMpApi(WxMpProperties wxMpProperties) {
+        log.info("wxMpProperties:" + wxMpProperties);
+        WxMpApi.wxMpProperties = wxMpProperties;
+    }
+
+    /**
+     * 微信小程序登录
+     *
+     * @param code
+     * @return
+     * @throws Exception
+     */
+    public static String getOpenid(String code) {
+        log.info("微信小程序code：" + code);
+        String url = "https://api.weixin.qq.com/sns/jscode2session";
+        Map<String, Object> paramMap = new HashMap<>();
+        paramMap.put("appid", wxMpProperties.getAppid());
+        paramMap.put("secret", wxMpProperties.getSecret());
+        paramMap.put("js_code", code);
+        paramMap.put("grant_type", "authorization_code");
+        String result = HttpUtil.get(url, paramMap);
+        log.info("微信小程序登录结果：" + result);
+        JSONObject jsonObject = JSONObject.parseObject(result);
+        String openid = jsonObject.getString("openid");
+        log.info("微信小程序登录openid：" + openid);
+        String errcode = jsonObject.getString("errcode");
+        String errmsg = jsonObject.getString("errmsg");
+        if (StringUtils.isBlank(openid)) {
+            log.error("微信小程序登录失败,errcode:{},errmsg", errcode, errmsg);
+            throw new BusinessException("微信小程序登录失败");
+        }
+        return openid;
+    }
+
+}
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
new file mode 100644
index 00000000..5928bc69
--- /dev/null
+++ b/src/main/resources/application-dev.yml
@@ -0,0 +1,29 @@
+spring:
+  datasource:
+    url: jdbc:mysql://localhost:3306/spring_boot_plus?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
+    username: root
+    password: root
+  redis:
+    host: 127.0.0.1
+    port: 6379
+    password:
+    database: 0
+
+# 打印SQL语句和结果集，本地开发环境可开启，线上注释掉
+mybatis-plus:
+  configuration:
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+
+# file上传配置
+file:
+  # 本地上传服务配置
+  local:
+    # 上传文件访问URL前缀
+    # 需修改为当前环境对外的ip地址
+    # 示例：http://localhost:8888/api/file
+    access-url: http://localhost:${server.port}/api/file
+
+# 日志配置，logback-spring.xml中会引用以下变量
+logback-spring:
+  # 日志文件目录
+  path: logs
diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml
new file mode 100644
index 00000000..66cee0cf
--- /dev/null
+++ b/src/main/resources/application-prod.yml
@@ -0,0 +1,38 @@
+spring:
+  datasource:
+    url: jdbc:mysql://localhost:3306/spring_boot_plus?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
+    username: root
+    password: root
+  redis:
+    host: 127.0.0.1
+    port: 6379
+    password:
+    database: 0
+
+# file上传配置
+file:
+  # 本地上传服务配置
+  local:
+    # 上传文件保存路径
+    upload-path: /home/upload/
+    # 上传文件访问URL前缀
+    # 需修改为当前环境对外的ip地址
+    # 示例：http://localhost:8888/api/file
+    access-url: http://localhost:${server.port}/api/file
+
+knife4j:
+  # 启用账号密码登录
+  basic:
+    enable: true
+    username: admin
+    password: 123456
+
+# 日志配置，logback-spring.xml中会引用以下变量
+logback-spring:
+  # 日志文件目录
+  path: /home/boot/logs
+
+# 日志AOP配置
+log-aop:
+  # 响应日志模式：FULL 全部，PART 部分，NONE 无
+  response-log-type: PART
\ No newline at end of file
diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml
new file mode 100644
index 00000000..3a8e24d4
--- /dev/null
+++ b/src/main/resources/application-test.yml
@@ -0,0 +1,31 @@
+spring:
+  datasource:
+    url: jdbc:mysql://localhost:3306/spring_boot_plus?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
+    username: root
+    password: root
+  redis:
+    host: 127.0.0.1
+    port: 6379
+    password:
+    database: 0
+
+# 打印SQL语句和结果集，本地开发环境可开启，线上注释掉
+mybatis-plus:
+  configuration:
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+
+# file上传配置
+file:
+  # 本地上传服务配置
+  local:
+    # 上传文件保存路径
+    upload-path: /home/upload/
+    # 上传文件访问URL前缀
+    # 需修改为当前环境对外的ip地址
+    # 示例：http://localhost:8888/api/file
+    access-url: http://localhost:${server.port}/api/file
+
+# 日志配置，logback-spring.xml中会引用以下变量
+logback-spring:
+  # 日志文件目录
+  path: /home/boot/logs
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 00000000..aa927d66
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,268 @@
+server:
+  port: 8888
+  servlet:
+    context-path: /api
+  tomcat:
+    uri-encoding: UTF-8
+    threads:
+      max: 1000
+      min-spare: 100
+    accept-count: 1000
+    max-connections: 10000
+
+# spring配置
+spring:
+  application:
+    name: spring-boot-plus
+  # 当前项目maven激活环境，例如：dev/test/prod
+  profiles:
+    active: dev
+  jackson:
+    date-format: yyy-MM-dd HH:mm:ss
+    time-zone: Asia/Shanghai
+  banner:
+    charset: UTF-8
+    location: classpath:banner.txt
+  servlet:
+    multipart:
+      max-file-size: 50MB
+      max-request-size: 500MB
+  # 数据源配置
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    type: com.zaxxer.hikari.HikariDataSource
+    hikari:
+      connection-timeout: 10000
+      minimum-idle: 10
+      maximum-pool-size: 20
+      idle-timeout: 30000
+      max-lifetime: 60000
+  # redis配置
+  redis:
+    host: 127.0.0.1
+    port: 6379
+    password:
+    # 连接超时时间（毫秒）
+    timeout: 10s
+    lettuce:
+      pool:
+        # 连接池最大连接数
+        max-active: 200
+        # 连接池中的最大空闲连接
+        max-idle: 20
+        # 连接池最大阻塞等待时间
+        max-wait: 10s
+        # 连接池中的最小空闲连接
+        min-idle: 10
+      shutdown-timeout: 3s
+
+# mybatis-plus配置
+mybatis-plus:
+  # 启动时是否检查MyBatis XML文件是否存在
+  check-config-location: true
+  # MyBatis原生配置
+  configuration:
+    # 字段名称下划线转驼峰命名
+    map-underscore-to-camel-case: true
+  global-config:
+    db-config:
+      # 全局默认主键类型
+      id-type: ASSIGN_ID
+      # 字段策略枚举类
+      # DEFAULT：默认不修改NULL值，ALWAYS：修改NULL值
+      update-strategy: DEFAULT
+  # mapper xml映射路径
+  mapper-locations: classpath*:mapper/**/*Mapper.xml
+
+# 分页
+pagehelper:
+  reasonable: false
+  support-methods-arguments: true
+  params: count=countSql
+
+# 主键ID生成配置
+# 机器码 如果一台服务器部署多个独立服务，需要为每个服务指定不同的 WorkerId
+# 取值0～63
+workerId: 0
+
+# 框架公共配置
+boot:
+  # 排除路径
+  exclude-paths:
+    - /v3/api-docs/**
+    - /swagger-resources/**,/swagger-ui/**,/doc.html
+    - /webjars/**
+    - /favicon.ico
+    - /index.html
+    - /file/**
+
+# 登录权限配置
+login:
+  # 排除路径
+  exclude-paths:
+    -
+  # 管理后台登录配置
+  admin:
+    # 是否启用管理后台登录权限校验
+    enable: true
+    # 是否单次登录
+    single-Login: false
+    # token过期时间，单位为分钟
+    token-expire-minutes: 120
+    # 是否校验目标方法权限
+    login-permission: false
+    # 包含的路径
+    include-paths:
+      - /admin/**
+    # 排除的路径
+    exclude-paths:
+      - /admin/login
+  # 用户端登录配置
+  app:
+    # 是否启用APP移动端登录权限校验
+    enable: true
+    # 是否单次登录
+    single-Login: false
+    # token过期时间，单位为天
+    token-expire-days: 7
+    # 登录拦截策略 LOGIN、IGNORE_LOGIN
+    # LOGIN：也可以在login-paths中指定
+    #   默认拦截include-paths路径开头的所有请求，如：/app/**
+    #   在不需要验证登录的controller方法加上@IgnoreLogin
+    #   表示此方法不需要登录就能调用
+    # IGNORE_LOGIN：也可以在ignore-login-paths中指定
+    #   默认不拦截include-paths路径开头的所有请求，如：/app/**
+    #   在需要登录的controller方法上加上注解@Login
+    #   表示此方法必须登录才能调用
+    login-intercept-strategy: LOGIN
+    # 包含路径
+    include-paths:
+      - /app/**
+    # 排除的路径：完全不需要登录校验，也不需要获取登录信息的路径
+    exclude-paths:
+      - /app/login,/app/accountLogin
+    # 需要登录校验的请求路径，与@Login效果一样
+    check-login-paths:
+      -
+    # 忽略登录校验的请求路径，与@IgnoreLogin效果一样
+    # 可登录也可不登录的情况下均可通过
+    ignore-login-paths:
+      - /app/logout
+  # 系统公共请求
+  common:
+    # 是否启用拦截器
+    enable: true
+    # 包含路径
+    include-paths:
+      - /common/**
+
+# 日志AOP配置
+log-aop:
+  # 是否启用
+  enable: true
+  # 是否打印日志
+  print-log: true
+  # 是否打印请求头
+  print-head-log: false
+  # 响应日志模式：FULL 全部，PART 部分，NONE 无
+  response-log-type: FULL
+  # 排除路径
+  excludePaths:
+    - /sysLog/**
+
+# XSS
+xss:
+  # 是否启用，默认禁用
+  enable: false
+  # 拦截的路径
+  url-patterns: /*
+  # 排序
+  order: 2
+  # 是否支持异步
+  async: true
+
+# redis项目前缀
+redis:
+  # 指定当前项目所有redisKey的前缀，注意：其它key不要以这个开头
+  # 目的为了区分多个项目在同一个db下的redisKey，避免混淆
+  # 例如：_boot_，表示当前项目所有的key都以此开头
+  # 如加上前缀后，当前项目下的所有key都将自动加上：_boot_.前缀
+  # 建议使用下划线开头和结束，目的是和其它key区分开
+  # 注意每个项目的前缀应保持唯一
+  # 可以不指定前缀，此时跟redis默认操作一样
+  # 如之前未指定前缀，后面指定了，则需要将之前的key清空，反之亦然
+  projectPrefix:
+
+# file上传配置
+file:
+  # 文件服务类型 本地还是OSS
+  file-server-type: LOCAL
+  # 本地上传服务配置
+  local:
+    # 上传文件访问路径
+    access-path: /file/**
+    # 上传文件保存路径，注意：最后带上反斜杠
+    # 如果为空，默认为当前项目路径下的upload目录
+    # 示例：/Users/mrliu/geekidea/spring-boot-plus/upload/
+    upload-path:
+    # 上传文件访问URL前缀
+    # 示例：http://localhost:8888/api/file
+    access-url: http://localhost:${server.port}/api/file
+  # OSS配置
+  oss:
+    accessKeyId:
+    accessKeySecret:
+    endpoint:
+    bucketName:
+    rootDir:
+    accessDomain: 
+
+# 微信小程序配置
+wx:
+  mp:
+    # AppID(小程序ID)
+    appid:
+    # AppSecret(小程序密钥)
+    secret:
+
+# 多商户行级数据权限配置
+merchant-line:
+  # 商户ID列名称
+  merchant-id-column: merchant_id
+  # 包含的表名称,自动添加商户ID条件处理
+  include-tables:
+    - demo_product
+
+# swagger配置
+springdoc:
+  swagger-ui:
+    enabled: true
+knife4j:
+  enable: true
+  basic:
+    enable: false
+  setting:
+    # 自定义底部
+    enable-footer: false
+    enable-footer-custom: true
+    footer-custom-content: MIT License | Copyright © 2019-2023 [geekidea](https://geekidea.io)
+openapi:
+  title: Boot API Docs
+  description: spring-boot-plus后台接口文档
+  terms-of-service: https://github.com/geekidea
+  contact-name: geekidea
+  contact-url: https://geekidea.io
+  contact-email: geekidea@qq.com
+  version: 1.0
+  external-description: spring-boot-plus
+  external-url: https://springboot.plus
+
+# 日志配置，logback-spring.xml中会引用以下变量
+logback-spring:
+  # 日志文件目录
+  path: logs
+  # info级别文件名称(包含error日志，方便查看)
+  info-file-name: boot-info
+  # error级别文件名称(只有error日志)
+  error-file-name: boot-error
+
diff --git a/config/src/main/resources/config/banner.txt b/src/main/resources/banner.txt
similarity index 96%
rename from config/src/main/resources/config/banner.txt
rename to src/main/resources/banner.txt
index 751d44b9..e953fe85 100644
--- a/config/src/main/resources/config/banner.txt
+++ b/src/main/resources/banner.txt
@@ -9,5 +9,5 @@ ${AnsiColor.BLUE}
      |_|                 |___/                                    |_|
 
 ${AnsiColor.YELLOW}      :: Spring Boot ::             ${AnsiColor.WHITE}(v${spring-boot.version})
-${AnsiColor.YELLOW}      :: spring-boot-plus ::        ${AnsiColor.WHITE}(v${project.version})
+${AnsiColor.YELLOW}      :: spring-boot-plus ::        ${AnsiColor.WHITE}(v2.7.18)
       https://springboot.plus
diff --git a/src/main/resources/generator.yml b/src/main/resources/generator.yml
new file mode 100644
index 00000000..b661ecc3
--- /dev/null
+++ b/src/main/resources/generator.yml
@@ -0,0 +1,46 @@
+# 生成代码默认配置
+generator:
+  # 包名称
+  packageName: io.geekidea.boot
+  # 模块名称
+  moduleName: test
+  # 作者
+  author: geekidea
+  # 生成ID类型： IdType
+  idType: ASSIGN_ID
+  # 排除的表前缀
+  tablePrefixes: tb_,b_,c_,test_
+  # 是否生成后端代码
+  generatorBackend: true
+  # 是否生成App后端代码
+  generatorAppBackend: true
+  # 是否生成前端代码
+  generatorFrontend: true
+  # 是否开启字段校验注解
+  validateField: true
+  # 是否启用日志注解
+  enableLog: true
+  # 是否生成权限编码
+  enablePermission: true
+  # 请求映射风格 DEFAULT、RESTFUL、SMALL_LETTER、HYPHEN、UNDERLINE
+  requestMappingStyle: DEFAULT
+  # 默认降序排列名称，如id、create_time，如果为空，则不指定默认排序
+  defaultOrderColumnName: id
+  # 上级菜单ID
+  parentMenuId: 0
+  # 表单布局方式 ONE：一行一列，TWO：一行两列
+  formLayout: TWO
+  # 生成方式 ZIP：zip压缩包，CUSTOM：自定义路径
+  generatorType: ZIP
+  # 自定义生成后端路径，如果生成方式为自定义路径，为空时，默认当前项目路径
+  customBackendPath:
+  # 自定义生成前端路径
+  customFrontendPath:
+  # 是否显示默认查询条件
+  showDefaultQuery: true
+  # 是否只生成实体类
+  onlyGeneratorEntity: false
+  # 搜索框模糊查询字段
+  keywordFields: name
+
+
diff --git a/src/main/resources/ip2region.xdb b/src/main/resources/ip2region.xdb
new file mode 100644
index 00000000..9f6502bb
Binary files /dev/null and b/src/main/resources/ip2region.xdb differ
diff --git a/config/src/main/resources/config/logback.xml b/src/main/resources/logback-spring.xml
similarity index 62%
rename from config/src/main/resources/config/logback.xml
rename to src/main/resources/logback-spring.xml
index dbe0c968..2cc5cfbc 100644
--- a/config/src/main/resources/config/logback.xml
+++ b/src/main/resources/logback-spring.xml
@@ -1,38 +1,27 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
 <configuration>
 
-    <property name="CONTEXT_NAME" value="spring-boot-plus"/>
-    <property name="LOG_PATH" value="logs"/>
+    <!-- 文件路径 -->
+    <springProperty name="LOG_PATH" source="logback-spring.path" defaultValue="logs" />
+    <!-- 日志文件名称 -->
+    <springProperty name="INFO_FILE_NAME" source="logback-spring.info-file-name" defaultValue="info" />
+    <springProperty name="ERROR_FILE_NAME" source="logback-spring.error-file-name" defaultValue="error" />
+
+    <!-- 单个文件最大大小 -->
     <property name="MAX_FILE_SIZE" value="10MB"/>
+    <!-- 最大保存时间 -->
     <property name="MAX_HISTORY" value="30"/>
 
-    <contextName>${CONTEXT_NAME}</contextName>
-
     <!-- 彩色日志 -->
     <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
     <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
     <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
 
     <!-- 控制台日志样式 -->
-    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr([%15.15t]){faint} [%X{requestId}] %clr(%-40.40logger{39}){cyan} [%L] %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
+    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint}%clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(%-26.26logger:%L{26}){cyan}[%X{traceId}][%X{ip}] %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
+
     <!-- 文件日志样式 -->
-    <property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} [%t] [%X{requestId}] %-40.40logger{39} %L : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
+    <property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}${LOG_LEVEL_PATTERN:-%5p} [%t] %-40.40logger{39}:%L [%X{traceId}][%X{ip}] %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
 
     <!-- 禁用logback自身日志输出 -->
     <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
@@ -49,9 +38,9 @@
         <encoder>
             <pattern>${FILE_LOG_PATTERN}</pattern>
         </encoder>
-        <file>${LOG_PATH}/spring-boot-plus.log</file>
+        <file>${LOG_PATH}/${INFO_FILE_NAME}.log</file>
         <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
-            <fileNamePattern>${LOG_PATH}/spring-boot-plus-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <fileNamePattern>${LOG_PATH}/${INFO_FILE_NAME}-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
             <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
             <maxHistory>${MAX_HISTORY}</maxHistory>
         </rollingPolicy>
@@ -62,9 +51,9 @@
         <encoder>
             <pattern>${FILE_LOG_PATTERN}</pattern>
         </encoder>
-        <file>${LOG_PATH}/spring-boot-plus-error.log</file>
+        <file>${LOG_PATH}/${ERROR_FILE_NAME}.log</file>
         <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
-            <fileNamePattern>${LOG_PATH}/spring-boot-plus-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <fileNamePattern>${LOG_PATH}/${ERROR_FILE_NAME}-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
             <maxFileSize>${MAX_FILE_SIZE}</maxFileSize>
             <maxHistory>${MAX_HISTORY}</maxHistory>
         </rollingPolicy>
@@ -87,14 +76,6 @@
         <appender-ref ref ="ERROR_FILE"/>
     </appender>
 
-    <!-- 不同环境的日志级别配置 -->
-    <springProfile name="dev">
-        <logger name="io.geekidea.springbootplus" level="DEBUG"/>
-    </springProfile>
-
-    <!-- 解决SpringBootAdmin错误日志问题 -->
-    <logger name="org.apache.catalina.connector.CoyoteAdapter" level="OFF"/>
-
     <root level="INFO">
         <appender-ref ref="CONSOLE" />
         <appender-ref ref="ASYNC_FILE" />
diff --git a/src/main/resources/mapper/generator/GeneratorColumnMapper.xml b/src/main/resources/mapper/generator/GeneratorColumnMapper.xml
new file mode 100644
index 00000000..2634cd1a
--- /dev/null
+++ b/src/main/resources/mapper/generator/GeneratorColumnMapper.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.generator.mapper.GeneratorColumnMapper">
+
+    <select id="getDbColumnListByTableName" resultType="io.geekidea.boot.generator.vo.GeneratorColumnDbVo">
+        select TABLE_NAME,
+               COLUMN_NAME,
+               COLUMN_COMMENT,
+               DATA_TYPE,
+               COLUMN_TYPE,
+               ORDINAL_POSITION                 columnSort,
+               if(COLUMN_KEY = 'PRI', 1, 0)     isPk,
+               if(IS_NULLABLE = 'NO', 1, 0)     isRequired,
+               if(COLUMN_DEFAULT is null, 0, 1) isDefaultValue
+        from information_schema.COLUMNS
+        where TABLE_SCHEMA = (select database())
+          and TABLE_NAME = #{tableName}
+        order by ORDINAL_POSITION
+    </select>
+
+    <select id="getGeneratorColumnList" resultType="io.geekidea.boot.generator.entity.GeneratorColumn">
+        select *
+        from generator_column
+        where table_name = #{tableName}
+        order by column_sort
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/generator/GeneratorTableMapper.xml b/src/main/resources/mapper/generator/GeneratorTableMapper.xml
new file mode 100644
index 00000000..f16c086d
--- /dev/null
+++ b/src/main/resources/mapper/generator/GeneratorTableMapper.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.generator.mapper.GeneratorTableMapper">
+
+    <select id="getGeneratorTableByTableName" resultType="io.geekidea.boot.generator.entity.GeneratorTable">
+        select *
+        from generator_table
+        where table_name = #{tableName}
+    </select>
+
+    <select id="getDbTableByTableName" resultType="io.geekidea.boot.generator.vo.GeneratorTableDbVo">
+        select TABLE_NAME, TABLE_COMMENT, CREATE_TIME
+        from information_schema.TABLES
+        where TABLE_SCHEMA = (select database())
+          and TABLE_NAME not like 'generator_%'
+          and TABLE_NAME = #{tableName}
+    </select>
+
+    <select id="getDbTablePage" parameterType="io.geekidea.boot.generator.query.GeneratorTableQuery"
+            resultType="io.geekidea.boot.generator.vo.GeneratorTableDbVo">
+        select it.TABLE_NAME,
+               it.TABLE_COMMENT,
+               it.CREATE_TIME,
+               gt.class_name,
+               gt.update_time,
+               gt.generator_type
+        from information_schema.TABLES it
+                 left join generator_table gt
+                           on it.TABLE_NAME = gt.table_name
+        where it.TABLE_SCHEMA = (select database())
+          and it.TABLE_NAME not like 'generator_%'
+        <if test="keyword != null and keyword != ''">
+            and (it.TABLE_NAME like concat('%',#{keyword},'%') or it.TABLE_COMMENT like concat('%',#{keyword},'%'))
+        </if>
+        order by it.CREATE_TIME desc
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/system/SysConfigMapper.xml b/src/main/resources/mapper/system/SysConfigMapper.xml
new file mode 100644
index 00000000..79272906
--- /dev/null
+++ b/src/main/resources/mapper/system/SysConfigMapper.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.system.mapper.SysConfigMapper">
+
+    <select id="getSysConfigById" resultType="io.geekidea.boot.system.vo.SysConfigVo">
+        select *
+        from sys_config
+        where id = #{id}
+    </select>
+
+    <select id="getSysConfigPage" parameterType="io.geekidea.boot.system.query.SysConfigQuery"
+            resultType="io.geekidea.boot.system.vo.SysConfigVo">
+        select *
+        from sys_config
+        <where>
+            <if test="keyword != null and keyword != ''">
+                and (config_name like concat('%',#{keyword},'%')
+                or config_key like concat('%',#{keyword},'%')
+                or config_value like concat('%',#{keyword},'%'))
+            </if>
+            <if test="status != null">
+                and status = #{status}
+            </if>
+            <if test="isSystem != null">
+                and is_system = #{isSystem}
+            </if>
+        </where>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/system/SysDictMapper.xml b/src/main/resources/mapper/system/SysDictMapper.xml
new file mode 100644
index 00000000..f7283c7f
--- /dev/null
+++ b/src/main/resources/mapper/system/SysDictMapper.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.system.mapper.SysDictMapper">
+
+    <select id="getSysDictById" resultType="io.geekidea.boot.system.vo.SysDictVo">
+        select *
+        from sys_dict
+        where id = #{id}
+    </select>
+
+    <select id="getSysDictPage" parameterType="io.geekidea.boot.system.query.SysDictQuery"
+            resultType="io.geekidea.boot.system.vo.SysDictVo">
+        select *
+        from sys_dict
+        where dict_code = #{dictCode}
+        <if test="keyword != null and keyword != ''">
+            and (value like concat('%',#{keyword},'%')
+            or label like concat('%',#{keyword},'%')
+            )
+        </if>
+        <if test="status != null">
+            and status = #{status}
+        </if>
+    </select>
+
+    <select id="getAppSysDictList" parameterType="io.geekidea.boot.system.query.SysDictAppQuery"
+            resultType="io.geekidea.boot.system.vo.AppSysDictVo">
+        select value, label, dict_code
+        from sys_dict
+        where status = 1
+        <if test="dictCode != null and dictCode != ''">
+            and dict_code = #{dictCode}
+        </if>
+        order by sort
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/system/SysDictTypeMapper.xml b/src/main/resources/mapper/system/SysDictTypeMapper.xml
new file mode 100644
index 00000000..8568ec0f
--- /dev/null
+++ b/src/main/resources/mapper/system/SysDictTypeMapper.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.system.mapper.SysDictTypeMapper">
+
+    <select id="getSysDictTypeById" resultType="io.geekidea.boot.system.vo.SysDictTypeVo">
+        select *
+        from sys_dict_type
+        where id = #{id}
+    </select>
+
+    <select id="getSysDictTypeList" parameterType="io.geekidea.boot.system.query.SysDictTypeQuery"
+            resultType="io.geekidea.boot.system.vo.SysDictTypeVo">
+        select *
+        from sys_dict_type
+        <where>
+            <if test="keyword != null and keyword != ''">
+                and (code like concat('%',#{keyword},'%')
+                or name like concat('%',#{keyword},'%')
+                )
+            </if>
+        </where>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/system/SysFileMapper.xml b/src/main/resources/mapper/system/SysFileMapper.xml
new file mode 100644
index 00000000..b6a3faba
--- /dev/null
+++ b/src/main/resources/mapper/system/SysFileMapper.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.system.mapper.SysFileMapper">
+
+    <select id="getSysFileById" resultType="io.geekidea.boot.system.vo.SysFileVo">
+        select *
+        from sys_file
+        where id = #{id}
+    </select>
+
+    <select id="getSysFilePage" parameterType="io.geekidea.boot.system.query.SysFileQuery"
+            resultType="io.geekidea.boot.system.vo.SysFileVo">
+        select *
+        from sys_file
+        <where>
+            <if test="fileType != null">
+                and file_type = #{fileType}
+            </if>
+            <if test="keyword != null and keyword != ''">
+                and (original_file_name like concat('%',#{keyword},'%')
+                or file_name like concat('%',#{keyword},'%'))
+            </if>
+            <if test="createStartTime != null">
+                and date(u.create_time) >= date(#{createStartTime})
+            </if>
+            <if test="createEndTime != null">
+                and date(u.create_time) &lt;= date(#{createEndTime})
+            </if>
+        </where>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/system/SysLogMapper.xml b/src/main/resources/mapper/system/SysLogMapper.xml
new file mode 100644
index 00000000..e1f63c86
--- /dev/null
+++ b/src/main/resources/mapper/system/SysLogMapper.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.system.mapper.SysLogMapper">
+
+    <select id="getSysLogById" resultType="io.geekidea.boot.system.vo.SysLogVo">
+        select *
+        from sys_log
+        where id = #{id}
+    </select>
+
+    <select id="getSysLogPage" parameterType="io.geekidea.boot.system.query.SysLogQuery"
+            resultType="io.geekidea.boot.system.vo.SysLogVo">
+        select *
+        from sys_log
+        <where>
+            <if test="moduleName != null and moduleName != ''">
+                module_name like concat('%',#{moduleName},'%')
+            </if>
+            <if test="requestUrl != null and requestUrl != ''">
+                and request_url like concat('%',#{requestUrl},'%')
+            </if>
+            <if test="logName != null and logName != ''">
+                and log_name like concat('%',#{logName},'%')
+            </if>
+            <if test="username != null and username != ''">
+                and username like concat('%',#{username},'%')
+            </if>
+            <if test="logType != null">
+                and log_type = #{logType}
+            </if>
+            <if test="responseSuccess != null">
+                and response_success = #{responseSuccess}
+            </if>
+            <if test="requestIp != null and requestIp != ''">
+                and request_ip like concat('%',#{requestIp},'%')
+            </if>
+            <if test="createStartTime != null">
+                and date(create_time) >= date(#{createStartTime})
+            </if>
+            <if test="createEndTime != null">
+                and date(create_time) &lt;= date(#{createEndTime})
+            </if>
+        </where>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/system/SysMenuMapper.xml b/src/main/resources/mapper/system/SysMenuMapper.xml
new file mode 100644
index 00000000..6a65bd00
--- /dev/null
+++ b/src/main/resources/mapper/system/SysMenuMapper.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.system.mapper.SysMenuMapper">
+
+    <select id="getSysMenuById" resultType="io.geekidea.boot.system.vo.SysMenuVo">
+        select *
+        from sys_menu
+        where id = #{id}
+    </select>
+
+    <select id="getPermissionCodesByUserId" resultType="java.lang.String">
+        select distinct m.code
+        from sys_user u
+                 inner join sys_role r
+                            on r.id = u.role_id
+                 inner join sys_role_menu rm
+                            on r.id = rm.role_id
+                 inner join sys_menu m
+                            on m.id = rm.menu_id
+        where u.status = 1
+          and m.status = 1
+          and m.code is not null
+          and u.id = #{userId}
+    </select>
+
+    <select id="getSysMenuTreeList" parameterType="io.geekidea.boot.system.query.SysMenuQuery"
+            resultType="io.geekidea.boot.system.vo.SysMenuTreeVo">
+        select *
+        from sys_menu
+        <where>
+            <if test="type != null">
+                and type = #{type}
+            </if>
+            <if test="status != null">
+                and status = #{status}
+            </if>
+            <if test="isShow != null">
+                and is_show = #{isShow}
+            </if>
+            <if test="keyword != null and keyword != ''">
+                and (name like concat('%',#{keyword},'%')
+                or code like concat('%',#{keyword},'%')
+                or route_url like concat('%',#{keyword},'%')
+                or component_path like concat('%',#{keyword},'%')
+                or route_redirect like concat('%',#{keyword},'%')
+                )
+            </if>
+        </where>
+        order by sort
+    </select>
+
+    <select id="getNavMenuTreeAllList" resultType="io.geekidea.boot.system.vo.SysNavMenuTreeVo">
+        select *
+        from sys_menu
+        where status = 1
+          and type in (1, 2)
+        order by sort
+    </select>
+
+    <select id="getNavMenuTreeList" resultType="io.geekidea.boot.system.vo.SysNavMenuTreeVo">
+        select distinct m.*
+        from sys_user u
+                 inner join sys_role r
+                            on r.id = u.role_id
+                 inner join sys_role_menu rm
+                            on r.id = rm.role_id
+                 inner join sys_menu m
+                            on m.id = rm.menu_id
+        where u.status = 1
+          and m.status = 1
+          and type in (1, 2)
+          and u.id = #{userId}
+        order by m.sort
+    </select>
+
+    <select id="getMenuIdsByRoleId" resultType="java.lang.Long">
+        select distinct m.id
+        from sys_role r
+                 inner join sys_role_menu rm
+                            on r.id = rm.role_id
+                 inner join sys_menu m
+                            on m.id = rm.menu_id
+        where m.status = 1
+          and rm.is_choice = 1
+          and r.id = #{roleId}
+    </select>
+
+    <select id="getChildrenMenuIds" resultType="java.lang.Long">
+        select id
+        from sys_menu
+        where parent_id in
+        <foreach collection="parentIds" item="parentId" open="(" close=")" separator=",">
+            #{parentId}
+        </foreach>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/system/SysRoleMapper.xml b/src/main/resources/mapper/system/SysRoleMapper.xml
new file mode 100644
index 00000000..80fc185d
--- /dev/null
+++ b/src/main/resources/mapper/system/SysRoleMapper.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.system.mapper.SysRoleMapper">
+
+    <select id="getSysRoleById" resultType="io.geekidea.boot.system.vo.SysRoleVo">
+        select *
+        from sys_role
+        where id = #{id}
+    </select>
+
+    <select id="getSysRolePage" parameterType="io.geekidea.boot.system.query.SysRoleQuery"
+            resultType="io.geekidea.boot.system.vo.SysRoleVo">
+        select *
+        from sys_role
+        <where>
+            <if test="isSystem != null">
+                and is_system = #{isSystem}
+            </if>
+            <if test="keyword !=null and keyword !=''">
+                and (name like concat('%',#{keyword},'%')
+                or code like concat('%',#{keyword},'%'))
+            </if>
+        </where>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/src/main/resources/mapper/system/SysRoleMenuMapper.xml
new file mode 100644
index 00000000..d3366f87
--- /dev/null
+++ b/src/main/resources/mapper/system/SysRoleMenuMapper.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.system.mapper.SysRoleMenuMapper">
+
+</mapper>
diff --git a/src/main/resources/mapper/system/SysUserMapper.xml b/src/main/resources/mapper/system/SysUserMapper.xml
new file mode 100644
index 00000000..cb6230b0
--- /dev/null
+++ b/src/main/resources/mapper/system/SysUserMapper.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.system.mapper.SysUserMapper">
+
+    <select id="getSysUserById" resultType="io.geekidea.boot.system.vo.SysUserVo">
+        select u.*, r.name roleName
+        from sys_user u
+                 inner join sys_role r
+                            on u.role_id = r.id
+        where u.id = #{id}
+    </select>
+
+    <select id="getSysUserPage" parameterType="io.geekidea.boot.system.query.SysUserQuery"
+            resultType="io.geekidea.boot.system.vo.SysUserVo">
+        select u.*,
+        r.name roleName,
+        r.is_system roleIsSystem
+        from sys_user u
+        inner join sys_role r
+        on u.role_id = r.id
+        <where>
+            <if test="roleId != null">
+                and r.id = #{roleId}
+            </if>
+            <if test="status != null">
+                and u.status = #{status}
+            </if>
+            <if test="startCreateTime != null">
+                and date(u.create_time) >= date(#{startCreateTime})
+            </if>
+            <if test="endCreateTime != null">
+                and date(u.create_time) &lt;= date(#{endCreateTime})
+            </if>
+            <if test="keyword != null and keyword != ''">
+                and (u.username like concat('%',#{keyword},'%')
+                or u.nickname like concat('%',#{keyword},'%')
+                or u.phone like concat('%',#{keyword},'%')
+                or u.email like concat('%',#{keyword},'%'))
+            </if>
+        </where>
+    </select>
+
+    <select id="getSysUserByUsername" resultType="io.geekidea.boot.system.entity.SysUser">
+        select u.*
+        from sys_user u
+        where u.username = #{username}
+    </select>
+
+    <select id="getCountByRoleId" resultType="java.lang.Integer">
+        select count(1)
+        from sys_user
+        where role_id = #{roleId}
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/user/UserMapper.xml b/src/main/resources/mapper/user/UserMapper.xml
new file mode 100644
index 00000000..77a05e3d
--- /dev/null
+++ b/src/main/resources/mapper/user/UserMapper.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.user.mapper.UserMapper">
+
+    <select id="getUserById" resultType="io.geekidea.boot.user.vo.UserVo">
+        select *
+        from user
+        where id = #{id}
+    </select>
+
+    <select id="getUserPage" parameterType="io.geekidea.boot.user.query.UserQuery"
+            resultType="io.geekidea.boot.user.vo.UserVo">
+        select *
+        from user
+    </select>
+
+    <select id="getAppUserById" resultType="io.geekidea.boot.user.vo.AppUserVo">
+        select u.*, r.name userRoleName
+        from user u
+                 left join user_role r
+                            on u.user_role_id = r.id
+        where u.id = #{id}
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/user/UserRoleMapper.xml b/src/main/resources/mapper/user/UserRoleMapper.xml
new file mode 100644
index 00000000..02eb5f0f
--- /dev/null
+++ b/src/main/resources/mapper/user/UserRoleMapper.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="io.geekidea.boot.user.mapper.UserRoleMapper">
+
+    <select id="getUserRoleById" resultType="io.geekidea.boot.user.vo.UserRoleVo">
+        select *
+        from user_role
+            where id = #{id}
+    </select>
+
+    <select id="getUserRolePage" parameterType="io.geekidea.boot.user.query.UserRoleQuery"
+            resultType="io.geekidea.boot.user.vo.UserRoleVo">
+        select *
+        from user_role
+    </select>
+
+</mapper>
diff --git a/src/main/resources/static/favicon.ico b/src/main/resources/static/favicon.ico
new file mode 100644
index 00000000..34679a32
Binary files /dev/null and b/src/main/resources/static/favicon.ico differ
diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html
new file mode 100644
index 00000000..03347758
--- /dev/null
+++ b/src/main/resources/static/index.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Spring Boot Plus</title>
+</head>
+<body>
+<h1>
+    <a href="/doc.html">系统接口文档</a>
+</h1>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/resources/templates/api.ts.vm b/src/main/resources/templates/api.ts.vm
new file mode 100644
index 00000000..3eedc3ca
--- /dev/null
+++ b/src/main/resources/templates/api.ts.vm
@@ -0,0 +1,47 @@
+import {http} from '@/utils/http'
+
+// $!{addCnComment}
+export function $!{addMethodName}(data:any) {
+    return http.post<any>('$!{controllerRequestPath}$!{addRequestPath}', data)
+}
+
+// $!{updateCnComment}
+export function $!{updateMethodName}(data:any) {
+    return http.post<any>('$!{controllerRequestPath}$!{updateRequestPath}', data)
+}
+
+// $!{deleteCnComment}
+export function $!{deleteMethodName}(id:string) {
+    return http.post<any>('$!{controllerRequestPath}$!{deleteRequestPath}'+id)
+}
+
+// $!{infoCnComment}
+export function $!{infoMethodName}(id:any) {
+    return http.post<any>('$!{controllerRequestPath}$!{infoRequestPath}'+id)
+}
+
+// $!{pageCnComment}
+export function $!{pageMethodName}(data:any) {
+    return http.post<any>('$!{controllerRequestPath}$!{pageRequestPath}', data)
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/templates/appController.java.vm b/src/main/resources/templates/appController.java.vm
new file mode 100644
index 00000000..44bd7d4d
--- /dev/null
+++ b/src/main/resources/templates/appController.java.vm
@@ -0,0 +1,67 @@
+package $!{appControllerPackage};
+
+import $!{pagingPackagePath};
+import $!{apiResultPackagePath};
+import $!{appQueryPackagePath};
+import $!{servicePackagePath};
+import $!{appVoPackagePath};
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+#if($!{requestMappingStyle} == 2)
+import $!{parameterObjectPackagePath};
+#end
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+#if($!{existsSerializableType})
+import java.io.Serializable;
+#end
+
+/**
+ * $!{appTableComment} 控制器
+ *
+ * @author $!{author}
+ * @since $!{date}
+ */
+@Slf4j
+@RestController
+@Tag(name = "$!{appTableComment}")
+$!{appControllerRequestMapping}
+public class $!{appControllerName} {
+
+    @Autowired
+    private $!{serviceName} $!{serviceObjectName};
+
+    /**
+     * $!{appInfoCnComment}
+     *
+     * @param $!{pkIdPropertyName}
+     * @return
+     * @throws Exception
+     */
+    @Operation(summary = "$!{appInfoCnComment}")
+    $!{appInfoRequestMapping}
+    public ApiResult<$!{appVoName}> $!{appInfoMethodName}(@PathVariable $!{pkIdPropertyType} $!{pkIdPropertyName}) {
+        log.info("$!{appInfoCnComment}：{}", $!{pkIdPropertyName});
+        $!{appVoName} $!{appVoObjectName} = $!{serviceObjectName}.$!{appInfoMethodName}ById($!{pkIdPropertyName});
+        return ApiResult.success($!{appVoObjectName});
+    }
+
+    /**
+     * $!{appPageCnComment}
+     *
+     * $!{queryObjectName}
+     * @return
+     * @throws Exception
+     */
+    @Operation(summary = "$!{appPageCnComment}")
+    $!{appPageRequestMapping}
+    public ApiResult<$!{appVoName}> $!{appPageMethodName}(@Valid $!{pageParamAnnotation} $!{appQueryName} $!{appQueryObjectName}) {
+        log.info("$!{appPageCnComment}：{}", $!{appQueryObjectName});
+        Paging<$!{appVoName}> paging = $!{serviceObjectName}.$!{appPageMethodName}($!{appQueryObjectName});
+        return ApiResult.success(paging);
+    }
+
+}
diff --git a/src/main/resources/templates/appQuery.java.vm b/src/main/resources/templates/appQuery.java.vm
new file mode 100644
index 00000000..aa5cc24a
--- /dev/null
+++ b/src/main/resources/templates/appQuery.java.vm
@@ -0,0 +1,20 @@
+package $!{queryPackage};
+
+import $!{superPageQueryPackagePath};
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * $!{appTableComment}查询参数
+ *
+ * @author $!{author}
+ * @since $!{date}
+ */
+@Data
+@Schema(description = "$!{appTableComment}查询参数")
+public class $!{appQueryName} extends $!{superPageQueryName} {
+
+    private static final long serialVersionUID = 1L;
+
+}
+
diff --git a/src/main/resources/templates/appVo.java.vm b/src/main/resources/templates/appVo.java.vm
new file mode 100644
index 00000000..cfd9e99e
--- /dev/null
+++ b/src/main/resources/templates/appVo.java.vm
@@ -0,0 +1,36 @@
+package $!{voPackage};
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+#if($!{existsBigDecimalType})
+import java.math.BigDecimal;
+#end
+#if($!{existsTimeType})
+import java.sql.Time;
+#end
+#if($!{existsDateType})
+import java.util.Date;
+#end
+
+/**
+ * $!{appTableComment}查询结果
+ *
+ * @author $!{author}
+ * @since $!{date}
+ */
+@Data
+@Schema(description = "$!{appTableComment}查询结果")
+public class $!{appVoName} implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+#foreach($column in $!{columns})
+
+    #if("$column.columnComment" != "")
+    @Schema(description = "$!{column.columnComment}")
+    #end
+    private $!{column.propertyType} $!{column.propertyName};
+#end
+
+}
diff --git a/src/main/resources/templates/controller.java.vm b/src/main/resources/templates/controller.java.vm
new file mode 100644
index 00000000..d0959ebc
--- /dev/null
+++ b/src/main/resources/templates/controller.java.vm
@@ -0,0 +1,144 @@
+package $!{controllerPackage};
+
+#if($!{enablePermission})
+import $!{permissionPackagePath};
+#end
+#if($!{enableLog})
+import $!{sysLogTypePackagePath};
+import $!{logPackagePath};
+#end
+import $!{pagingPackagePath};
+import $!{apiResultPackagePath};
+import $!{dtoPackagePath};
+import $!{queryPackagePath};
+import $!{servicePackagePath};
+import $!{voPackagePath};
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+#if($!{requestMappingStyle} == 2)
+import $!{parameterObjectPackagePath};
+#end
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+#if($!{existsSerializableType})
+import java.io.Serializable;
+#end
+
+/**
+ * $!{tableComment} 控制器
+ *
+ * @author $!{author}
+ * @since $!{date}
+ */
+@Slf4j
+@RestController
+@Tag(name = "$!{tableComment}")
+$!{controllerRequestMapping}
+public class $!{controllerName} {
+
+    @Autowired
+    private $!{serviceName} $!{serviceObjectName};
+
+    /**
+     * $!{addCnComment}
+     *
+     * @param $!{dtoObjectName}
+     * @return
+     * @throws Exception
+     */
+    #if($!{enableLog})
+    @Log(type = SysLogType.ADD)
+    #end
+    @Operation(summary = "$!{addCnComment}")
+    $!{addRequestMapping}
+    #if($!{enablePermission})
+    @Permission("$!{addPermissionCode}")
+    #end
+    public ApiResult $!{addMethodName}(@Valid @RequestBody $!{dtoName} $!{dtoObjectName}) {
+        log.info("$!{addCnComment}：{}", $!{dtoObjectName});
+        boolean flag = $!{serviceObjectName}.$!{addMethodName}($!{dtoObjectName});
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * $!{updateCnComment}
+     *
+     * @param $!{dtoObjectName}
+     * @return
+     * @throws Exception
+     */
+    #if($!{enableLog})
+    @Log(type = SysLogType.UPDATE)
+    #end
+    @Operation(summary = "$!{updateCnComment}")
+    $!{updateRequestMapping}
+    #if($!{enablePermission})
+    @Permission("$!{updatePermissionCode}")
+    #end
+    public ApiResult $!{updateMethodName}(@Valid @RequestBody $!{dtoName} $!{dtoObjectName}) {
+        log.info("$!{updateCnComment}：{}", $!{dtoObjectName});
+        boolean flag = $!{serviceObjectName}.$!{updateMethodName}($!{dtoObjectName});
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * $!{deleteCnComment}
+     *
+     * @param $!{pkIdPropertyName}
+     * @return
+     * @throws Exception
+     */
+    #if($!{enableLog})
+    @Log(type = SysLogType.DELETE)
+    #end
+    @Operation(summary = "$!{deleteCnComment}")
+    $!{deleteRequestMapping}
+    #if($!{enablePermission})
+    @Permission("$!{deletePermissionCode}")
+    #end
+    public ApiResult $!{deleteMethodName}(@PathVariable $!{pkIdPropertyType} $!{pkIdPropertyName}) {
+        log.info("$!{deleteCnComment}：{}", $!{pkIdPropertyName});
+        boolean flag = $!{serviceObjectName}.$!{deleteMethodName}($!{pkIdPropertyName});
+        return ApiResult.result(flag);
+    }
+
+    /**
+     * $!{infoCnComment}
+     *
+     * @param $!{pkIdPropertyName}
+     * @return
+     * @throws Exception
+     */
+    @Operation(summary = "$!{infoCnComment}")
+    $!{infoRequestMapping}
+    #if($!{enablePermission})
+    @Permission("$!{infoPermissionCode}")
+    #end
+    public ApiResult<$!{voName}> $!{infoMethodName}(@PathVariable $!{pkIdPropertyType} $!{pkIdPropertyName}) {
+        log.info("$!{infoCnComment}：{}", $!{pkIdPropertyName});
+        $!{voName} $!{voObjectName} = $!{serviceObjectName}.$!{infoMethodName}ById($!{pkIdPropertyName});
+        return ApiResult.success($!{voObjectName});
+    }
+
+    /**
+     * $!{pageCnComment}
+     *
+     * @param $!{queryObjectName}
+     * @return
+     * @throws Exception
+     */
+    @Operation(summary = "$!{pageCnComment}")
+    $!{pageRequestMapping}
+    #if($!{enablePermission})
+    @Permission("$!{pagePermissionCode}")
+    #end
+    public ApiResult<$!{voName}> $!{pageMethodName}(@Valid $!{pageParamAnnotation} $!{queryName} $!{queryObjectName}) {
+        log.info("$!{pageCnComment}：{}", $!{queryObjectName});
+        Paging<$!{voName}> paging = $!{serviceObjectName}.$!{pageMethodName}($!{queryObjectName});
+        return ApiResult.success(paging);
+    }
+
+}
diff --git a/src/main/resources/templates/dto.java.vm b/src/main/resources/templates/dto.java.vm
new file mode 100644
index 00000000..69e4ea16
--- /dev/null
+++ b/src/main/resources/templates/dto.java.vm
@@ -0,0 +1,55 @@
+package $!{dtoPackage};
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+#if($!{existsValidate})
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+#end
+import java.io.Serializable;
+#if($!{existsBigDecimalType})
+import java.math.BigDecimal;
+#end
+#if($!{existsTimeType})
+import java.sql.Time;
+#end
+#if($!{existsDateType})
+import java.util.Date;
+#end
+
+/**
+ * $!{tableComment}参数
+ *
+ * @author $!{author}
+ * @since $!{date}
+ */
+@Data
+@Schema(description = "$!{tableComment}参数")
+public class $!{dtoName} implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+#foreach($column in $!{columns})
+#if(!$!{commonExcludeFields.contains($!{column.propertyName})})
+
+    #if($!{column.columnComment} != "")
+    @Schema(description = "$!{column.columnComment}")
+    #end
+    #if($!{column.isValidate})
+    #if($!{column.propertyType} == 'String')
+    @NotBlank(message = "$!{column.columnCustomComment}不能为空")
+    #if($!{column.existsLength})
+    @Length(max = $!{column.columnLength}, message = "$!{column.columnComment}长度超过限制")
+    #end
+    #else
+    @NotNull(message = "$!{column.columnCustomComment}不能为空")
+    #end
+    #end
+    private $!{column.propertyType} $!{column.propertyName};
+#end
+#end
+
+}
+
+
diff --git a/src/main/resources/templates/entity.java.vm b/src/main/resources/templates/entity.java.vm
new file mode 100644
index 00000000..34581595
--- /dev/null
+++ b/src/main/resources/templates/entity.java.vm
@@ -0,0 +1,46 @@
+package $!{entityPackage};
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+#if($!{existsBigDecimalType})
+import java.math.BigDecimal;
+#end
+#if($!{existsTimeType})
+import java.sql.Time;
+#end
+#if($!{existsDateType})
+import java.util.Date;
+#end
+
+/**
+ * $!{tableComment}
+ *
+ * @author $!{author}
+ * @since $!{date}
+ */
+@Data
+@TableName("$!{tableName}")
+@Schema(description = "$!{tableComment}")
+public class $!{entity} implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+#foreach($column in $!{columns})
+
+    #if($!{column.columnComment} != "")
+    @Schema(description = "$!{column.columnComment}")
+    #end
+    #if($!{column.isPk})
+    @TableId(value = "$!{column.columnName}", type = IdType.$!{idType})
+    #elseif($!{fillInsertFields.contains($!{column.propertyName})})
+    @TableField(fill = FieldFill.INSERT)
+    #elseif($!{fillUpdateFields.contains($!{column.propertyName})})
+    @TableField(fill = FieldFill.UPDATE)
+    #end
+    private $!{column.propertyType} $!{column.propertyName};
+#end
+
+}
+
diff --git a/src/main/resources/templates/mapper.java.vm b/src/main/resources/templates/mapper.java.vm
new file mode 100644
index 00000000..82d44a63
--- /dev/null
+++ b/src/main/resources/templates/mapper.java.vm
@@ -0,0 +1,61 @@
+package $!{mapperPackage};
+
+import $!{superMapperPackagePath};
+import $!{entityPackagePath};
+import $!{queryPackagePath};
+import $!{voPackagePath};
+#if($!{generatorAppBackend})
+import $!{appQueryPackagePath};
+import $!{appVoPackagePath};
+#end
+import org.apache.ibatis.annotations.Mapper;
+
+#if($!{existsSerializableType})
+import java.io.Serializable;
+#end
+import java.util.List;
+
+/**
+ * $!{tableComment} Mapper 接口
+ *
+ * @author $!{author}
+ * @since $!{date}
+ */
+@Mapper
+public interface $!{mapperName} extends $!{superMapperName}<$!{entity}> {
+
+    /**
+     * $!{tableComment}详情
+     *
+     * @param $!{pkIdPropertyName}
+     * @return
+     */
+    $!{voName} $!{infoMethodName}ById($!{pkIdPropertyType} $!{pkIdPropertyName});
+
+    /**
+     * $!{tableComment}分页列表
+     *
+     * @param $!{queryObjectName}
+     * @return
+     */
+    List<$!{voName}> $!{pageMethodName}($!{queryName} $!{queryObjectName});
+
+#if($!{generatorAppBackend})
+    /**
+     * $!{appTableComment}详情
+     *
+     * @param $!{pkIdPropertyName}
+     * @return
+     */
+    $!{appVoName} $!{appInfoMethodName}ById($!{pkIdPropertyType} $!{pkIdPropertyName});
+
+    /**
+     * $!{appTableComment}分页列表
+     *
+     * @param $!{queryObjectName}
+     * @return
+     */
+    List<$!{appVoName}> $!{appPageMethodName}($!{appQueryName} $!{queryObjectName});
+
+#end
+}
diff --git a/src/main/resources/templates/mapper.xml.vm b/src/main/resources/templates/mapper.xml.vm
new file mode 100644
index 00000000..bab3b093
--- /dev/null
+++ b/src/main/resources/templates/mapper.xml.vm
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="$!{mapperPackagePath}">
+
+    <select id="$!{infoMethodName}ById" resultType="$!{voPackagePath}">
+        select *
+        from $!{tableName}
+        where $!{pkIdColumnName} = #{$!{pkIdPropertyName}}
+    </select>
+
+    <select id="$!{pageMethodName}" parameterType="$!{queryPackagePath}"
+            resultType="$!{voPackagePath}">
+        select *
+        from $!{tableName}
+        <where>
+    #if($!{keywordColumns.size()} > 0)
+        #set($keywordCount=0)
+            <if test="keyword != null and keyword != ''">
+            #foreach($keywordColumn in $!{keywordColumns})
+                #set($keywordCount=$keywordCount+1)
+                #if($keywordCount==1)                and (#end
+                #if($keywordCount != 1)                or #end$!{keywordColumn.columnName} like concat('%',#{keyword},'%')#if($keywordCount==$!{keywordColumns.size()}))#end
+            #end
+            </if>
+    #end
+    #foreach($column in $!{queryColumns})
+        #if($!{column.propertyType} == "String")
+            #set($condition="$!{column.propertyName} != null and $!{column.propertyName} != ''")
+        #else
+            #set($condition="$!{column.propertyName} != null")
+        #end
+        #if($!{column.queryType} == 1)
+            <if test="$condition">
+                and $!{column.columnName} = #{$!{column.propertyName}}
+            </if>
+        #elseif($!{column.queryType} == 2)
+            <if test="$condition">
+                and $!{column.columnName} != #{$!{column.propertyName}}
+            </if>
+        #elseif($!{column.queryType} == 3)
+            <if test="$condition">
+                and $!{column.columnName} gt; #{$!{column.propertyName}}
+            </if>
+        #elseif($!{column.queryType} == 4)
+            <if test="$condition">
+                and $!{column.columnName} >= #{$!{column.propertyName}}
+            </if>
+        #elseif($!{column.queryType} == 5)
+            <if test="$condition">
+                and $!{column.columnName} lt; #{$!{column.propertyName}}
+            </if>
+        #elseif($!{column.queryType} == 6)
+            <if test="$condition">
+                and $!{column.columnName} le; #{$!{column.propertyName}}
+            </if>
+        #elseif($!{column.queryType} == 7)
+            <if test="$condition">
+                and $!{column.columnName} like concat('%',#{column.propertyName},'%'
+            </if>
+        #elseif($!{column.queryType} == 8)
+            <if test="$!{column.propertyName}Start != null">
+                and date($!{column.columnName}) >= date(#{$!{column.propertyName}Start})
+            </if>
+            <if test="$!{column.propertyName}End != null">
+                and date($!{column.columnName}) &lt;= date(#{$!{column.propertyName}End})
+            </if>
+        #elseif($!{column.queryType} == 9)
+            <if test="$!{column.propertyName}Start != null">
+                and $!{column.columnName} >= #{$!{column.propertyName}Start}
+            </if>
+            <if test="$!{column.propertyName}End != null">
+                and $!{column.columnName} &lt;= #{$!{column.propertyName}End}
+            </if>
+        #end
+    #end
+        </where>
+    </select>
+
+#if($!{generatorAppBackend})
+    <select id="$!{appInfoMethodName}ById" resultType="$!{appVoPackagePath}">
+        select *
+        from $!{tableName}
+        where $!{pkIdColumnName} = #{$!{pkIdPropertyName}}
+    </select>
+
+    <select id="$!{appPageMethodName}" parameterType="$!{appQueryPackagePath}"
+            resultType="$!{appVoPackagePath}">
+        select *
+        from $!{tableName}
+        <where>
+    #if($!{appKeywordColumns.size()} > 0)
+        #set($appKeywordCount=0)
+            <if test="keyword != null and keyword != ''">
+            #foreach($appKeywordColumn in $!{appKeywordColumns})
+                #set($appKeywordCount=$appKeywordCount+1)
+                #if($appKeywordCount==1)                and (#end
+                #if($appKeywordCount != 1)                or #end$!{appKeywordColumn.columnName} like concat('%',#{keyword},'%')#if($appKeywordCount==$!{appKeywordColumns.size()}))#end
+            #end
+            </if>
+    #end
+    #foreach($column in $!{queryColumns})
+        #if($!{column.propertyType} == "String")
+            #set($condition="$!{column.propertyName} != null and $!{column.propertyName} != ''")
+        #else
+            #set($condition="$!{column.propertyName} != null")
+        #end
+        #if($!{column.queryType} == 1)
+            <if test="$condition">
+                and $!{column.columnName} = #{$!{column.propertyName}}
+            </if>
+        #elseif($!{column.queryType} == 2)
+            <if test="$condition">
+                and $!{column.columnName} != #{$!{column.propertyName}}
+            </if>
+        #elseif($!{column.queryType} == 3)
+            <if test="$condition">
+                and $!{column.columnName} gt; #{$!{column.propertyName}}
+            </if>
+        #elseif($!{column.queryType} == 4)
+            <if test="$condition">
+                and $!{column.columnName} >= #{$!{column.propertyName}}
+            </if>
+        #elseif($!{column.queryType} == 5)
+            <if test="$condition">
+                and $!{column.columnName} lt; #{$!{column.propertyName}}
+            </if>
+        #elseif($!{column.queryType} == 6)
+            <if test="$condition">
+                and $!{column.columnName} le; #{$!{column.propertyName}}
+            </if>
+        #elseif($!{column.queryType} == 7)
+            <if test="$condition">
+                and $!{column.columnName} like concat('%',#{column.propertyName},'%'
+            </if>
+        #elseif($!{column.queryType} == 8)
+            <if test="$!{column.propertyName}Start != null">
+                and date($!{column.columnName}) >= date(#{$!{column.propertyName}Start})
+            </if>
+            <if test="$!{column.propertyName}End != null">
+                and date($!{column.columnName}) &lt;= date(#{$!{column.propertyName}End})
+            </if>
+        #elseif($!{column.queryType} == 9)
+            <if test="$!{column.propertyName}Start != null">
+                and $!{column.columnName} >= #{$!{column.propertyName}Start}
+            </if>
+            <if test="$!{column.propertyName}End != null">
+                and $!{column.columnName} &lt;= #{$!{column.propertyName}End}
+            </if>
+        #end
+    #end
+        </where>
+    </select>
+
+#end
+</mapper>
diff --git a/src/main/resources/templates/menu.sql.vm b/src/main/resources/templates/menu.sql.vm
new file mode 100644
index 00000000..4123b4b2
--- /dev/null
+++ b/src/main/resources/templates/menu.sql.vm
@@ -0,0 +1,12 @@
+-- 目录菜单
+INSERT INTO sys_menu (id, name, parent_id, type, code, route_url, component_path, route_redirect, icon, sort, is_show, is_cache, status, create_time) VALUES ($!{dirSysMenu.id}, '$!{dirSysMenu.name}', $!{dirSysMenu.parentId}, 1, null, '$!{dirSysMenu.routeUrl}', null, null, null, 0, 1, 0, 1, now());
+
+-- 菜单
+INSERT INTO sys_menu (id, name, parent_id, type, code, route_url, component_path, route_redirect, icon, sort, is_show, is_cache, status, create_time) VALUES ($!{sysMenu.id}, '$!{sysMenu.name}', $!{dirSysMenu.id}, 2, '$!{sysMenu.code}', '$!{sysMenu.routeUrl}', '$!{sysMenu.componentPath}', null, null, 0, 1, 0, 1, now());
+
+-- 权限
+#set($count=0)
+#foreach($menu in $!{permissionSysMenus})
+#set($count=$count+1)
+INSERT INTO sys_menu (id, name, parent_id, type, code, route_url, component_path, route_redirect, icon, sort, is_show, is_cache, status, create_time) VALUES ($!{menu.id}, '$!{menu.name}', $!{sysMenu.id}, 3, '$!{menu.code}', null, null, null, null, $count, 0, 0, 1, now());
+#end
diff --git a/src/main/resources/templates/query.java.vm b/src/main/resources/templates/query.java.vm
new file mode 100644
index 00000000..83c84b53
--- /dev/null
+++ b/src/main/resources/templates/query.java.vm
@@ -0,0 +1,44 @@
+package $!{queryPackage};
+
+import $!{superPageQueryPackagePath};
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+#if($!{existsBigDecimalType})
+import java.math.BigDecimal;
+#end
+#if($!{existsTimeType})
+import java.sql.Time;
+#end
+#if($!{existsDateType})
+import java.util.Date;
+#end
+
+/**
+ * $!{tableComment}查询参数
+ *
+ * @author $!{author}
+ * @since $!{date}
+ */
+@Data
+@Schema(description = "$!{tableComment}查询参数")
+public class $!{queryName} extends $!{superPageQueryName} {
+
+    private static final long serialVersionUID = 1L;
+
+#foreach($column in $!{queryColumns})
+#if($!{column.queryType} == 8 || $!{column.queryType} == 9)
+    @Schema(description = "开始的$!{column.columnCustomComment}")
+    private Date $!{column.propertyName}Start;
+
+    @Schema(description = "结束的$!{column.columnCustomComment}")
+    private Date $!{column.propertyName}End;
+
+#else
+    @Schema(description = "$!{column.columnCustomComment}")
+    private $!{column.propertyType} $!{column.propertyName};
+
+#end
+#end
+}
+
diff --git a/src/main/resources/templates/service.java.vm b/src/main/resources/templates/service.java.vm
new file mode 100644
index 00000000..d7496b83
--- /dev/null
+++ b/src/main/resources/templates/service.java.vm
@@ -0,0 +1,91 @@
+package $!{servicePackage};
+
+import $!{superServicePackagePath};
+import $!{pagingPackagePath};
+import $!{dtoPackagePath};
+import $!{entityPackagePath};
+import $!{queryPackagePath};
+import $!{voPackagePath};
+#if($!{generatorAppBackend})
+import $!{appQueryPackagePath};
+import $!{appVoPackagePath};
+#end
+
+#if($!{existsSerializableType})
+import java.io.Serializable;
+#end
+
+/**
+ * $!{tableComment} 服务接口
+ *
+ * @author $!{author}
+ * @since $!{date}
+ */
+public interface $!{serviceName} extends $!{superServiceName}<$!{entity}> {
+
+    /**
+     * 添加$!{tableComment}
+     *
+     * @param $!{dtoObjectName}
+     * @return
+     * @throws Exception
+     */
+    boolean $!{addMethodName}($!{dtoName} $!{dtoObjectName});
+
+    /**
+     * 修改$!{tableComment}
+     *
+     * @param $!{dtoObjectName}
+     * @return
+     * @throws Exception
+     */
+    boolean $!{updateMethodName}($!{dtoName} $!{dtoObjectName});
+
+    /**
+     * 删除$!{tableComment}
+     *
+     * @param $!{pkIdPropertyName}
+     * @return
+     * @throws Exception
+     */
+    boolean $!{deleteMethodName}($!{pkIdPropertyType} $!{pkIdPropertyName});
+
+    /**
+     * $!{tableComment}详情
+     *
+     * @param $!{pkIdPropertyName}
+     * @return
+     * @throws Exception
+     */
+    $!{voName} $!{infoMethodName}ById($!{pkIdPropertyType} $!{pkIdPropertyName});
+
+    /**
+     * $!{tableComment}分页列表
+     *
+     * @param $!{queryObjectName}
+     * @return
+     * @throws Exception
+     */
+    Paging<$!{voName}> $!{pageMethodName}($!{queryName} $!{queryObjectName});
+
+#if($!{generatorAppBackend})
+    /**
+     * $!{appTableComment}详情
+     *
+     * @param $!{pkIdPropertyName}
+     * @return
+     * @throws Exception
+     */
+    $!{appVoName} $!{appInfoMethodName}ById($!{pkIdPropertyType} $!{pkIdPropertyName});
+
+    /**
+     * $!{appTableComment}分页列表
+     *
+     * @param $!{queryObjectName}
+     * @return
+     * @throws Exception
+     */
+    Paging<$!{appVoName}> $!{appPageMethodName}($!{appQueryName} $!{queryObjectName});
+
+#end
+}
diff --git a/src/main/resources/templates/serviceImpl.java.vm b/src/main/resources/templates/serviceImpl.java.vm
new file mode 100644
index 00000000..b778cbe2
--- /dev/null
+++ b/src/main/resources/templates/serviceImpl.java.vm
@@ -0,0 +1,114 @@
+package $!{serviceImplPackage};
+
+import $!{superServiceImplPackagePath};
+import $!{businessExceptionPackagePath};
+import $!{orderByItemPackagePath};
+import $!{orderMappingPackagePath};
+import $!{pagingPackagePath};
+import $!{dtoPackagePath};
+import $!{entityPackagePath};
+import ${mapperPackagePath};
+import $!{queryPackagePath};
+import ${servicePackagePath};
+import $!{voPackagePath};
+#if($!{generatorAppBackend})
+import $!{appQueryPackagePath};
+import $!{appVoPackagePath};
+#end
+import $!{pagingUtilPackagePath};
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+#if($!{existsSerializableType})
+import java.io.Serializable;
+#end
+
+import java.util.List;
+
+/**
+ * $!{tableComment} 服务实现类
+ *
+ * @author $!{author}
+ * @since $!{date}
+ */
+@Slf4j
+@Service
+public class $!{serviceImplName} extends $!{superServiceImplName}<$!{mapperName}, $!{entity}> implements $!{serviceName} {
+
+    @Autowired
+    private $!{mapperName} $!{mapperObjectName};
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean $!{addMethodName}($!{dtoName} $!{dtoObjectName}) {
+        $!{entity} $!{entityObjectName} = new $!{entity}();
+        BeanUtils.copyProperties($!{dtoObjectName}, $!{entityObjectName});
+        return save($!{entityObjectName});
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean $!{updateMethodName}($!{dtoName} $!{dtoObjectName}) {
+        $!{pkIdPropertyType} $!{pkIdPropertyName} = $!{dtoObjectName}.get$!{pascalPkIdPropertyName}();
+        $!{entity} $!{entityObjectName} = getById($!{pkIdPropertyName});
+        if ($!{entityObjectName} == null) {
+            throw new BusinessException("$!{tableComment}不存在");
+        }
+        BeanUtils.copyProperties($!{dtoObjectName}, $!{entityObjectName});
+        return updateById($!{entityObjectName});
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public boolean $!{deleteMethodName}($!{pkIdPropertyType} $!{pkIdPropertyName}) {
+        return removeById($!{pkIdPropertyName});
+    }
+
+    @Override
+    public $!{voName} $!{infoMethodName}ById($!{pkIdPropertyType} $!{pkIdPropertyName}) {
+        return $!{mapperObjectName}.$!{infoMethodName}ById($!{pkIdPropertyName});
+    }
+
+    @Override
+    public Paging<$!{voName}> $!{pageMethodName}($!{queryName} $!{queryObjectName}) {
+        OrderMapping orderMapping = new OrderMapping();
+        #if($!{existsCreateTime})
+        orderMapping.put("createTime", "create_time");
+        #end
+        #if($!{defaultOrderColumnName})
+        PagingUtil.handlePage(query, orderMapping, OrderByItem.desc("$!{defaultOrderColumnName}"));
+        #else
+        PagingUtil.handlePage(query, orderMapping);
+        #end
+        List<$!{voName}> list = $!{mapperObjectName}.$!{pageMethodName}(query);
+        Paging<$!{voName}> paging = new Paging<>(list);
+        return paging;
+    }
+
+#if($!{generatorAppBackend})
+    @Override
+    public $!{appVoName} $!{appInfoMethodName}ById($!{pkIdPropertyType} $!{pkIdPropertyName}) {
+        return $!{mapperObjectName}.$!{appInfoMethodName}ById($!{pkIdPropertyName});
+    }
+
+    @Override
+    public Paging<$!{appVoName}> $!{appPageMethodName}($!{appQueryName} $!{queryObjectName}) {
+        OrderMapping orderMapping = new OrderMapping();
+        #if($!{existsCreateTime})
+        orderMapping.put("createTime", "create_time");
+        #end
+        #if($!{defaultOrderColumnName})
+        PagingUtil.handlePage(query, orderMapping, OrderByItem.desc("$!{defaultOrderColumnName}"));
+        #else
+        PagingUtil.handlePage(query, orderMapping);
+        #end
+        List<$!{appVoName}> list = $!{mapperObjectName}.$!{appPageMethodName}(query);
+        Paging<$!{appVoName}> paging = new Paging<>(list);
+        return paging;
+    }
+
+#end
+}
diff --git a/src/main/resources/templates/vo.java.vm b/src/main/resources/templates/vo.java.vm
new file mode 100644
index 00000000..21a2febe
--- /dev/null
+++ b/src/main/resources/templates/vo.java.vm
@@ -0,0 +1,37 @@
+package $!{voPackage};
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+#if($!{existsBigDecimalType})
+import java.math.BigDecimal;
+#end
+#if($!{existsTimeType})
+import java.sql.Time;
+#end
+#if($!{existsDateType})
+import java.util.Date;
+#end
+
+/**
+ * $!{tableComment}查询结果
+ *
+ * @author $!{author}
+ * @since $!{date}
+ */
+@Data
+@Schema(description = "$!{tableComment}查询结果")
+public class $!{voName} implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+#foreach($column in $!{columns})
+
+    #if("$!column.columnComment" != "")
+    @Schema(description = "$!{column.columnComment}")
+    #end
+    private $!{column.propertyType} $!{column.propertyName};
+#end
+
+}
+
diff --git a/src/main/resources/templates/vue.form.vm b/src/main/resources/templates/vue.form.vm
new file mode 100644
index 00000000..4365ec33
--- /dev/null
+++ b/src/main/resources/templates/vue.form.vm
@@ -0,0 +1,178 @@
+<template>
+  <el-dialog :model-value="dialogData.isShow" :title="dialogData.id?'编辑':'新增'"
+             @close="closeDialog" draggable>
+    <el-form :model="form" ref="formRef" :rules="rules" label-width="90px">
+      <el-row :gutter="10">
+#set($count=0)
+#foreach($column in $!{formColumns})
+    #set($count=$count+1)
+    #set($num=$count != 1 && $count != $!{formColumns.size()} && $count % 2 == 0)
+    #if($!{formLayout} == 1)
+        #set($colSpan=24)
+    #else
+        #set($colSpan=12)
+    #end
+    #set($endRow=$!{formLayout} == 2 && $num)
+    #if($!{column.formType} == 2)
+        <el-col :span="$colSpan">
+          <el-form-item label="$!{column.columnCustomComment}" prop="$!{column.propertyName}">
+            <el-input type="textarea" v-model="form.$!{column.propertyName}" maxlength="$!{column.columnLength}" placeholder="请输入$!{column.columnCustomComment}"/>
+          </el-form-item>
+        </el-col>
+    #elseif($!{column.formType} == 3)
+        <el-col :span="$colSpan">
+          <el-form-item label="$!{column.columnCustomComment}" prop="$!{column.propertyName}">
+            <el-input type="number" v-model="form.$!{column.propertyName}" placeholder="请输入$!{column.columnCustomComment}"/>
+          </el-form-item>
+        </el-col>
+    #elseif($!{column.formType} == 4)
+        <el-col :span="$colSpan">
+          <el-form-item label="$!{column.columnCustomComment}">
+            <el-radio-group v-model="form.$!{column.propertyName}">
+            #foreach($option in $!{column.options})
+              <el-radio :label="$!{option.value}" border>$!{option.label}</el-radio>
+            #end
+            </el-radio-group>
+          </el-form-item>
+        </el-col>
+    #elseif($!{column.formType} == 6)
+        <el-col :span="$colSpan">
+          <el-form-item label="$!{column.columnCustomComment}" prop="$!{column.propertyName}">
+            <el-select v-model="form.$!{column.propertyName}" clearable placeholder="请选择$!{column.columnCustomComment}">
+            #foreach($option in $!{column.options})
+              <el-option value="$!{option.value}" label="$!{option.label}"/>
+            #end
+            </el-select>
+          </el-form-item>
+        </el-col>
+    #elseif($!{column.formType} == 7)
+        <el-col :span="$colSpan">
+          <el-form-item label="$!{column.columnCustomComment}">
+            <el-date-picker v-model="form.$!{column.propertyName}" type="date" placeholder="请选择$!{column.columnCustomComment}"/>
+          </el-form-item>
+        </el-col>
+    #elseif($!{column.formType} == 8)
+         <el-col :span="$colSpan">
+           <el-form-item label="$!{column.columnCustomComment}">
+             <el-date-picker v-model="form.$!{column.propertyName}" type="datetime" placeholder="请选择$!{column.columnCustomComment}"/>
+           </el-form-item>
+         </el-col>
+    #else
+         <el-col :span="$colSpan">
+           <el-form-item label="$!{column.columnCustomComment}" prop="$!{column.propertyName}">
+             <el-input v-model="form.$!{column.propertyName}" maxlength="$!{column.columnLength}" placeholder="请输入$!{column.columnCustomComment}"/>
+           </el-form-item>
+         </el-col>
+    #end
+    #if($endRow)
+      </el-row>
+      <el-row :gutter="10">
+    #end
+#end
+      </el-row>
+    </el-form>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button type="primary" @click="submit">确定</el-button>
+        <el-button @click="closeDialog">取消</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import {ElMessage, FormInstance, FormRules} from "element-plus";
+import {$!{addMethodName}, $!{infoMethodName}, $!{updateMethodName}} from "@$!{vueApiPath}";
+
+const formRef = ref<FormInstance>()
+
+const emits = defineEmits<{
+  (event: 'refresh'): void
+}>()
+
+// 表单
+let form: any = ref({
+    $!{pkColumn.propertyName}: null,
+#foreach($column in $!{formColumns})
+    $!{column.propertyName}:#if($!{column.formType} == 4)true#else null#end,
+#end
+});
+
+// 效验规则
+const rules = reactive<FormRules>({
+#foreach($column in $!{columns})
+#if(!$!{commonExcludeFields.contains($!{column.propertyName})})
+#if(!$!{column.isPk} && $!{column.isRequired})
+  $!{column.propertyName}: [
+#if($!{column.formType} == 1 || $!{column.formType} == 2 || $!{column.formType} == 3 || $!{column.formType} == 11)
+    {required: true, message: '请输入$!{column.columnCustomComment}', trigger: 'blur'},
+#else
+    {required: true, message: '请选择$!{column.columnCustomComment}', trigger: 'blur'},
+#end
+  ],
+#end
+#end
+#end
+})
+
+// 获取详情
+const getDetails = (id: string) => {
+  $!{infoMethodName}(id).then(res => {
+    form.value = Object.assign({}, form.value, res);
+  })
+}
+
+// 弹框数据
+const dialogData = reactive({
+  isShow: false,
+  id: null
+})
+
+// 打开弹框
+const openDialog = async (id: string) => {
+  dialogData.isShow = true;
+  dialogData.title = '新增$!{tableComment}';
+  if (id) {
+    dialogData.id = id;
+    dialogData.title = '编辑$!{tableComment}';
+    getDetails(id);
+  }
+}
+
+// 关闭弹框
+const closeDialog = () => {
+  dialogData.isShow = false;
+  form.value = {};
+}
+// 提交
+
+const submit = async () => {
+   if (!formRef.value) return;
+   await formRef.value.validate((valid: any) => {
+   if (valid) {
+       let data = form.value;
+     if (data.id) {
+       $!{updateMethodName}({...data}).then(() => {
+         ElMessage.success('操作成功');
+         closeDialog();
+         emits('refresh');
+       })
+     } else {
+       $!{addMethodName}({...data}).then(() => {
+       ElMessage.success('操作成功');
+       closeDialog();
+       emits('refresh');
+       })
+     }
+   }
+  })
+}
+
+defineExpose({
+  openDialog,
+});
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/main/resources/templates/vue.index.vm b/src/main/resources/templates/vue.index.vm
new file mode 100644
index 00000000..f4c5cc12
--- /dev/null
+++ b/src/main/resources/templates/vue.index.vm
@@ -0,0 +1,240 @@
+<template>
+  <el-card shadow="never" class="card-box">
+    <el-form :model="queryForm" label-width="80px">
+      <el-row :gutter="20">
+#foreach($column in $!{queryColumns})
+    #if($!{column.formType} == 4 || $!{column.formType} == 6)
+        <el-col :sm="24" :md="12" :lg="8" :xl="6">
+          <el-form-item label="$!{column.columnCustomComment}">
+            <el-select v-model="queryForm.$!{column.propertyName}" clearable placeholder="请选择$!{column.columnCustomComment}">
+            #foreach($option in $!{column.options})
+              <el-option value="$!{option.value}" label="$!{option.label}"/>
+            #end
+            </el-select>
+          </el-form-item>
+        </el-col>
+    #elseif($!{column.formType} == 7 || $!{column.formType} == 8 )
+        <el-col :lg="8" :md="12" :sm="24" :xl="6">
+          <el-form-item label="$!{column.columnCustomComment}">
+            <custom-date-picker type="daterange" v-model:startDate="queryForm.$!{column.propertyName}Start"
+                                v-model:endDate="queryForm.$!{column.propertyName}End"
+                                clearable start-placeholder="开始时间"
+                                end-placeholder="结束时间"/>
+          </el-form-item>
+        </el-col>
+    #else
+        <el-col :sm="24" :md="12" :lg="8" :xl="6">
+          <el-form-item label="$!{column.columnCustomComment}">
+            <el-input v-model="queryForm.$!{column.propertyName}" @keyup.enter="onSearch" clearable placeholder="请输入$!{column.columnCustomComment}"/>
+          </el-form-item>
+        </el-col>
+    #end
+#end
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :sm="24" :md="12" :lg="8" :xl="6">
+          <el-form-item label="搜索">
+            <el-input v-model="queryForm.keyword" @keyup.enter="onSearch" clearable placeholder="请输入名称"/>
+          </el-form-item>
+        </el-col>
+        <el-col :sm="24" :md="12" :lg="8" :xl="6">
+          <el-form-item label-width="0">
+            <el-button type="primary" @click="onSearch">
+              <el-icon>
+                <ele-search/>
+              </el-icon>
+              <span class="search-btn__left">查询</span>
+            </el-button>
+            <el-button @click="onReset">
+              <el-icon>
+                <ele-refresh/>
+              </el-icon>
+              <span class="search-btn__left">重置</span>
+            </el-button>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+#if($!{enablePermission})
+    <div v-auth="'$!{addPermissionCode}'" class="table-btn-box mb10">
+#else
+    <div class="table-btn-box mb10">
+#end
+      <el-button type="primary" @click="openDialog()">
+        <el-icon class="mr5">
+          <ele-circle-plus/>
+        </el-icon>
+        新 增
+      </el-button>
+    </div>
+    <el-table v-loading="tableLoading.status" :data="tableData.data" border row-key="id" @sort-change="sortChange">
+      <el-table-column prop="_tableIndex" label="序号" align="center" width="50px"/>
+    #foreach($column in $!{tableColumns})
+    #if($!{column.formType} == 4)
+      <el-table-column prop="$!{column.propertyName}" label="$!{column.columnCustomComment}" align="center">
+        <template #default="scope">
+        #foreach($option in $!{column.options})
+          #if($!{option.value} == true)
+          <el-tag v-if="scope.row.$!{column.propertyName} == true" type="success" disable-transitions>$!{option.label}</el-tag>
+          #end
+          #if($!{option.value} == false)
+          <el-tag v-if="scope.row.$!{column.propertyName} == false" type="danger" disable-transitions>$!{option.label}</el-tag>
+          #end
+        #end
+        </template>
+      </el-table-column>
+    #elseif($!{column.formType} == 6)
+      <el-table-column prop="$!{column.propertyName}" label="$!{column.columnCustomComment}" align="center">
+        <template #default="scope">
+        #foreach($option in $!{column.options})
+          <span v-if="scope.row.$!{column.propertyName} == $!{option.value}">$!{option.label}</span>
+        #end
+        </template>
+      </el-table-column>
+    #elseif($!{column.formType} == 7)
+      <el-table-column prop="$!{column.propertyName}" label="$!{column.columnCustomComment}" align="center" min-width="150"/>
+    #elseif($!{column.formType} == 8)
+      #if($!{existsCreateTime})
+      <el-table-column prop="$!{column.propertyName}" label="$!{column.columnCustomComment}" align="center" min-width="180" sortable="custom"/>
+      #else
+      <el-table-column prop="$!{column.propertyName}" label="$!{column.columnCustomComment}" align="center" min-width="180"/>
+      #end
+    #else
+      <el-table-column prop="$!{column.propertyName}" label="$!{column.columnCustomComment}" align="center" show-overflow-tooltip/>
+    #end
+    #end
+      <el-table-column label="操作" fixed="right" align="center" min-width="116">
+        <template #default="scope">
+        #if($!{enablePermission})
+          <el-button v-auth="'$!{updatePermissionCode}'" link type="primary" @click="openDialog(scope.row.$!{pkIdPropertyName})">
+        #else
+          <el-button link type="primary" @click="openDialog(scope.row.$!{pkIdPropertyName})">
+        #end
+            <el-icon>
+              <ele-edit/>
+            </el-icon>
+            修改
+          </el-button>
+        #if($!{enablePermission})
+          <el-button v-auth="'$!{deletePermissionCode}'" link type="primary" @click="delTable(scope.row.$!{pkIdPropertyName})">
+        #else
+          <el-button link type="primary" @click="delTable(scope.row.$!{pkIdPropertyName})">
+        #end
+            <el-icon>
+              <ele-delete/>
+            </el-icon>
+            删除
+          </el-button>
+
+        </template>
+      </el-table-column>
+    </el-table>
+    <CustomPagination v-model:currentPage="pagination.pageIndex" v-model:pageSize="pagination.pageSize"
+                      :total="pagination.total" @changePage="changePage"/>
+
+    <TableForm ref="tableDialogRef" @refresh="getTableList"/>
+
+  </el-card>
+</template>
+<script lang="ts" setup>
+import {$!{deleteMethodName}, $!{pageMethodName}} from "@$!{vueApiPath}";
+import {ElMessage, ElMessageBox} from 'element-plus'
+import TableForm from './form.vue'
+import {calcTableIndex} from "@/utils/util";
+
+/** 查询参数 **/
+let queryForm: any = ref({
+    keyword: null,
+#foreach($column in $!{queryColumns})
+    $!{column.propertyName}: null,
+#end
+});
+
+const tableLoading = ref({
+    status: false
+})
+
+// 查询
+const onSearch = () => {
+    pagination.pageIndex = 1;
+    getTableList();
+}
+// 重置
+const onReset = () => {
+    queryForm.value = {}
+    pagination.pageIndex = 1;
+    getTableList();
+}
+
+/** 分页*/
+// 分页数据
+const pagination = reactive({
+    pageIndex: 1,
+    pageSize: 10,
+    total: 0
+})
+// 翻页
+const changePage = (page: number) => {
+    pagination.pageIndex = page;
+    getTableList();
+}
+
+/** 排序*/
+const orderBy = ref({})
+// 排序
+const sortChange = ({column, prop, order}) => {
+    if (order) {
+        orderBy.value.orderByColumn = prop;
+        orderBy.value.orderByAsc = order === "ascending";
+    } else {
+        orderBy.value = {}
+    }
+    pagination.pageIndex = 1;
+    getTableList();
+}
+
+/** 表格*/
+// 表格数据
+const tableData = reactive({
+    data: [],
+})
+// 获取表格列表
+const getTableList = () => {
+    tableLoading.value.status = true;
+    $!{pageMethodName}({...pagination, ...queryForm.value, ...orderBy.value}).then(res => {
+        tableData.data = calcTableIndex(res, pagination);;
+        pagination.total = res.total;
+        tableLoading.value.status = false;
+    })
+}
+// 删除
+const delTable = (id: string) => {
+    ElMessageBox.confirm(
+            '是否确认删除本条数据？',
+            '提示',
+            {
+                confirmButtonText: '确认',
+                cancelButtonText: '取消',
+                type: 'warning',
+            })
+            .then(() => {
+                $!{deleteMethodName}(id).then(() => {
+                    ElMessage.success('删除成功');
+                    getTableList();
+                })
+            }).catch(() => {
+    })
+}
+
+/** 添加，编辑*/
+const tableDialogRef = ref()
+// 打开弹框
+const openDialog = async (id: string) => {
+    await tableDialogRef.value.openDialog(id);
+}
+
+getTableList();
+</script>
+<style scoped>
+
+</style>
diff --git a/src/test/java/io/geekidea/boot/generator/Generator.java b/src/test/java/io/geekidea/boot/generator/Generator.java
new file mode 100644
index 00000000..8bc3c797
--- /dev/null
+++ b/src/test/java/io/geekidea/boot/generator/Generator.java
@@ -0,0 +1,75 @@
+package io.geekidea.boot.generator;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import io.geekidea.boot.common.constant.SystemConstant;
+import io.geekidea.boot.config.properties.GeneratorProperties;
+import io.geekidea.boot.generator.enums.GeneratorFormLayout;
+import io.geekidea.boot.generator.enums.RequestMappingStyle;
+import io.geekidea.boot.generator.handler.GeneratorHandler;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 本地代码生成，与可视化生成共享代码模板
+ * 与可视化代码生成的相同点
+ * 1. 使用相同的代码生成模板
+ * 2. 使用相同的代码生成器
+ * 3. 生成的效果一样
+ * 与可视化代码生成的不同点
+ * 1. 直接读取数据库表和列的最新信息
+ * 2. 与可视化生成的generator_table、generator_column表没有关系
+ * 3. 可视化代码使用的mybatis访问数据库表和列信息
+ * 4. 本地代码生成使用的jdbc直接读取数据库表和列信息
+ *
+ * @author geekidea
+ * @date 2024/1/15
+ **/
+public class Generator {
+
+    public static void main(String[] args) throws Exception {
+        GeneratorProperties config = new GeneratorProperties();
+        // 包名称
+        config.setPackageName("io.geekidea.boot");
+        // 模块名称
+        config.setModuleName("test");
+        // 作者
+        config.setAuthor("geekidea");
+        // 生成ID类型枚举类型
+        config.setIdType(IdType.ASSIGN_ID);
+        // 生成的表名称
+        List<String> tableNames = Arrays.asList("");
+        // 排除的表前缀
+        config.setTablePrefixes(Arrays.asList("tb_", "b_", "c_", "test_"));
+        // 是否生成后台代码
+        config.setGeneratorBackend(true);
+        // 是否生成App后端代码
+        config.setGeneratorAppBackend(true);
+        // 是否生成前端代码
+        config.setGeneratorFrontend(true);
+        // 是否校验字段
+        config.setValidateField(true);
+        // 是否启用日志注解
+        config.setEnableLog(true);
+        // 是否生成权限编码
+        config.setEnablePermission(true);
+        // 是否显示默认查询条件
+        config.setShowDefaultQuery(true);
+        config.setKeywordFields(Arrays.asList("name"));
+        // 请求映射风格 DEFAULT、RESTFUL、SMALL_LETTER、HYPHEN、UNDERLINE
+        config.setRequestMappingStyle(RequestMappingStyle.DEFAULT);
+        // 设置默认降序排列名称，如id、create_time，如果为空，则不指定默认排序
+        config.setDefaultOrderColumnName("id");
+        // 设置父菜单ID
+        config.setParentMenuId(SystemConstant.ROOT_MENU_ID);
+        // 设置前端表单布局方式 1：一行一列，2：一行两列
+        config.setFormLayout(GeneratorFormLayout.TWO);
+        // 前端自定义代码生成绝对路径，默认为当前项目下的vue目录，可配置如：/Users/mrliu/geekidea/spring-boot-plus-vue3
+        config.setCustomFrontendPath("/");
+        // 设置是否只生成或覆盖实体类
+        config.setOnlyGeneratorEntity(false);
+        // 生成代码
+        GeneratorHandler.generatorCode(config, tableNames);
+    }
+
+}
diff --git a/src/test/java/io/geekidea/boot/generator/SimpleGenerator.java b/src/test/java/io/geekidea/boot/generator/SimpleGenerator.java
new file mode 100644
index 00000000..1355be0d
--- /dev/null
+++ b/src/test/java/io/geekidea/boot/generator/SimpleGenerator.java
@@ -0,0 +1,24 @@
+package io.geekidea.boot.generator;
+
+import io.geekidea.boot.generator.config.SimpleGeneratorConfig;
+import io.geekidea.boot.generator.handler.SimpleGeneratorHandler;
+
+/**
+ * @author geekidea
+ * @date 2022/7/2
+ **/
+public class SimpleGenerator {
+
+    public static void main(String[] args) throws Exception {
+        SimpleGeneratorConfig config = new SimpleGeneratorConfig();
+        config.setModuleName("user");
+        config.setName("UserSearch");
+        config.setDesc("用户描述");
+        config.setAuthor("geekidea");
+        config.setGenerateController(true);
+        config.setGenerateService(true);
+        config.setGenerateMapper(true);
+        SimpleGeneratorHandler.generateSimple(config);
+    }
+
+}
diff --git a/src/test/java/io/geekidea/boot/generator/config/SimpleGeneratorConfig.java b/src/test/java/io/geekidea/boot/generator/config/SimpleGeneratorConfig.java
new file mode 100644
index 00000000..48cf947b
--- /dev/null
+++ b/src/test/java/io/geekidea/boot/generator/config/SimpleGeneratorConfig.java
@@ -0,0 +1,39 @@
+package io.geekidea.boot.generator.config;
+
+import lombok.Data;
+
+/**
+ * @author geekidea
+ * @date 2022/7/2
+ **/
+@Data
+public class SimpleGeneratorConfig {
+    /**
+     * 名称
+     */
+    private String name;
+    /**
+     * 备注
+     */
+    private String desc;
+    /**
+     * 作者
+     */
+    private String author;
+    /**
+     * 模块名称
+     */
+    private String moduleName;
+    /**
+     * 是否生成controller，默认生成
+     */
+    private boolean generateController = true;
+    /**
+     * 是否生成service及serviceImpl，默认生成
+     */
+    private boolean generateService = true;
+    /**
+     * 是否生成mapper及xml，默认生成
+     */
+    private boolean generateMapper = true;
+}
diff --git a/src/test/java/io/geekidea/boot/generator/handler/GeneratorHandler.java b/src/test/java/io/geekidea/boot/generator/handler/GeneratorHandler.java
new file mode 100644
index 00000000..2cff05f6
--- /dev/null
+++ b/src/test/java/io/geekidea/boot/generator/handler/GeneratorHandler.java
@@ -0,0 +1,213 @@
+package io.geekidea.boot.generator.handler;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import io.geekidea.boot.config.properties.GeneratorProperties;
+import io.geekidea.boot.generator.constant.GeneratorConstant;
+import io.geekidea.boot.generator.entity.GeneratorColumn;
+import io.geekidea.boot.generator.entity.GeneratorTable;
+import io.geekidea.boot.generator.enums.GeneratorFormLayout;
+import io.geekidea.boot.generator.enums.GeneratorType;
+import io.geekidea.boot.generator.enums.RequestMappingStyle;
+import io.geekidea.boot.generator.exception.GeneratorException;
+import io.geekidea.boot.generator.jdbc.JdbcUtil;
+import io.geekidea.boot.generator.util.GeneratorUtil;
+import io.geekidea.boot.generator.vo.GeneratorColumnDbVo;
+import io.geekidea.boot.generator.vo.GeneratorTableDbVo;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.File;
+import java.util.List;
+import java.util.Scanner;
+
+/**
+ * @author geekidea
+ * @date 2024/1/15
+ **/
+@Slf4j
+public class GeneratorHandler {
+
+    /**
+     * 生成代码
+     *
+     * @param config
+     * @param tableNames
+     * @throws Exception
+     */
+    public static void generatorCode(GeneratorProperties config, List<String> tableNames) throws Exception {
+        if (config == null) {
+            throw new GeneratorException("配置不能为空");
+        }
+        if (CollectionUtils.isEmpty(tableNames)) {
+            throw new GeneratorException("表名称不能为空");
+        }
+        int blankLength = 0;
+        for (String tableName : tableNames) {
+            if (StringUtils.isBlank(tableName)) {
+                blankLength++;
+            }
+        }
+        if (blankLength == tableNames.size()) {
+            throw new GeneratorException("表名称不能为空");
+        }
+        // 检查配置
+        checkConfig(config);
+        // 检查所有的表是否已存在实体类
+        boolean isFileOverride = isFileOverride(config, tableNames);
+        if (isFileOverride) {
+            // 循环生成代码
+            try {
+                for (String tableName : tableNames) {
+                    if (StringUtils.isBlank(tableName)) {
+                        continue;
+                    }
+                    GeneratorTable generatorTable = getGeneratorTable(config, tableName);
+                    GeneratorUtil.generatorCode(tableName, generatorTable);
+                }
+                log.info("代码生成成功");
+            } catch (Exception e) {
+                e.printStackTrace();
+                log.error("代码生成失败");
+            }
+        }
+    }
+
+    /**
+     * 判断代码是否存在，如果存在，则提示输入y(yes)进行覆盖，输入其它则跳过，不会再生成代码
+     * 如果不存在，则继续生成代码
+     *
+     * @param config
+     * @param tableNames
+     * @return
+     */
+    private static boolean isFileOverride(GeneratorProperties config, List<String> tableNames) {
+        boolean isExists = checkExistsEntityCode(config, tableNames);
+        if (isExists) {
+            System.err.println("如需覆盖生成，请输入y(yes)继续覆盖生成代码，输入其它则跳过，不会再生成代码：");
+            Scanner scanner = new Scanner(System.in);
+            String line = scanner.nextLine();
+            if (StringUtils.isNotBlank(line)) {
+                line = line.toLowerCase();
+                if (GeneratorConstant.Y.equals(line) || GeneratorConstant.YES.equals(line)) {
+                    System.out.println("继续覆盖生成代码");
+                    return true;
+                } else {
+                    System.out.println("跳过代码生成");
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 检查所有的表是否已存在实体类
+     *
+     * @param config
+     */
+    private static boolean checkExistsEntityCode(GeneratorProperties config, List<String> tableNames) {
+        String moduleName = config.getModuleName();
+        boolean isExists = false;
+        for (String tableName : tableNames) {
+            String className = GeneratorUtil.getTableClassName(tableName, config.getTablePrefixes());
+            // 判断代码是否存在
+            String userDir = GeneratorConstant.USER_DIR;
+            String packageName = config.getPackageName();
+            String packagePath = packageName.replaceAll("\\.", "/");
+            String entityPath = userDir + GeneratorConstant.SRC_MAIN_JAVA + packagePath + "/" + moduleName + "/" + GeneratorConstant.ENTITY_PACKAGE + "/" + className + GeneratorConstant.DOT_JAVA;
+            File file = new File(entityPath);
+            if (file.exists()) {
+                isExists = true;
+                System.err.println(tableName + "表对应的实体类" + className + "已经存在：" + entityPath);
+            }
+        }
+        return isExists;
+    }
+
+
+    /**
+     * 检查配置
+     *
+     * @param config
+     */
+    public static void checkConfig(GeneratorProperties config) throws Exception {
+        String packageName = config.getPackageName();
+        if (StringUtils.isBlank(packageName)) {
+            throw new GeneratorException("包名称不能为空");
+        }
+        String moduleName = config.getModuleName();
+        if (StringUtils.isBlank(moduleName)) {
+            throw new GeneratorException("模块名称不能为空");
+        }
+        String author = config.getAuthor();
+        if (StringUtils.isBlank(author)) {
+            throw new GeneratorException("作者不能为空");
+        }
+        // 默认为ASSIGN_ID
+        IdType idType = config.getIdType();
+        if (idType == null) {
+            config.setIdType(IdType.ASSIGN_ID);
+        }
+        boolean generatorBackend = config.isGeneratorBackend();
+        boolean generatorAppBackend = config.isGeneratorAppBackend();
+        boolean generatorFrontend = config.isGeneratorFrontend();
+        if (!generatorBackend && !generatorAppBackend && !generatorFrontend) {
+            throw new GeneratorException("前后端代码生成配置都为false，跳过代码生成");
+        }
+        if (generatorAppBackend && !generatorBackend) {
+            throw new GeneratorException("需要设置generatorBackend为true，才能生成App后台代码");
+        }
+        // 默认请求映射风格
+        RequestMappingStyle requestMappingStyle = config.getRequestMappingStyle();
+        if (requestMappingStyle == null) {
+            config.setRequestMappingStyle(RequestMappingStyle.DEFAULT);
+        }
+        String defaultOrderColumnName = config.getDefaultOrderColumnName();
+        if (StringUtils.isBlank(defaultOrderColumnName)) {
+            config.setDefaultOrderColumnName(GeneratorConstant.ID);
+        }
+        // 设置默认的上级菜单ID为0
+        Long parentMenuId = config.getParentMenuId();
+        if (parentMenuId == null) {
+            config.setParentMenuId(0L);
+        }
+        // 设置默认表单布局方式为 2：一行两列
+        GeneratorFormLayout generatorFormLayout = config.getFormLayout();
+        if (generatorFormLayout == null) {
+            config.setFormLayout(GeneratorFormLayout.TWO);
+        }
+        // 生成方式 1：zip压缩包，2：自定义路径
+        config.setGeneratorType(GeneratorType.CUSTOM);
+    }
+
+    /**
+     * 获取生成代码的表信息
+     *
+     * @param config
+     * @param tableName
+     * @return
+     * @throws Exception
+     */
+    public static GeneratorTable getGeneratorTable(GeneratorProperties config, String tableName) throws Exception {
+        // 获取数据库中的表信息
+        GeneratorTableDbVo generatorTableDbVo = JdbcUtil.getGeneratorTableDbVo(tableName);
+        if (generatorTableDbVo == null) {
+            throw new GeneratorException(tableName + "表不存在或不能生成");
+        }
+        // 获取数据库中表的列信息集合
+        List<GeneratorColumnDbVo> generatorColumnDbVos = JdbcUtil.getGeneratorColumnDbVos(tableName);
+        if (CollectionUtils.isEmpty(generatorColumnDbVos)) {
+            throw new GeneratorException(tableName + "列为空，跳过代码生成");
+        }
+        GeneratorTable table = new GeneratorTable();
+        // 设置表基础信息
+        GeneratorUtil.setGeneratorTable(config, tableName, generatorTableDbVo, table);
+        // 设置列信息
+        boolean validateField = config.isValidateField();
+        List<GeneratorColumn> columns = GeneratorUtil.getGeneratorColumns(tableName, generatorColumnDbVos, validateField);
+        GeneratorUtil.setTableColumnInfo(table, columns);
+        return table;
+    }
+
+}
diff --git a/src/test/java/io/geekidea/boot/generator/handler/RenameHandler.java b/src/test/java/io/geekidea/boot/generator/handler/RenameHandler.java
new file mode 100644
index 00000000..607791c4
--- /dev/null
+++ b/src/test/java/io/geekidea/boot/generator/handler/RenameHandler.java
@@ -0,0 +1,239 @@
+package io.geekidea.boot.generator.handler;
+
+import io.geekidea.boot.generator.constant.GeneratorConstant;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 重命名和替换包名称
+ *
+ * @author geekidea
+ * @date 2023/11/24
+ **/
+@Slf4j
+public class RenameHandler {
+
+    /**
+     * @param currentPackageName
+     * @param targetPackageName
+     * @param targetArtifactId
+     * @throws Exception
+     */
+    public static void execute(String currentPackageName, String targetPackageName, String targetArtifactId) throws Exception {
+        log.info("currentPackageName:" + currentPackageName);
+        log.info("targetPackageName:" + targetPackageName);
+        log.info("targetArtifactId:" + targetArtifactId);
+        if (StringUtils.isBlank(currentPackageName)) {
+            throw new RuntimeException("当前包名称不能为空" );
+        }
+        if (StringUtils.isBlank(targetPackageName)) {
+            throw new RuntimeException("目标包名称不能为空" );
+        }
+        if (StringUtils.isBlank(targetArtifactId)) {
+            targetArtifactId = GeneratorConstant.DEFAULT_PROJECT_NAME;
+        }
+        // 校验包名格式
+        if (currentPackageName.equals(targetPackageName)) {
+            throw new RuntimeException("包名称一致，无须修改" );
+        }
+        String projectPath = System.getProperty("user.dir" );
+        log.info("projectPath = " + projectPath);
+        String currentPackagePath = currentPackageName.replaceAll("\\.", "/");
+        String targetPackagePath = targetPackageName.replaceAll("\\.", "/");
+        log.info("currentPackagePath = " + currentPackagePath);
+
+        // 替换pom.xml内容
+        log.info("替换pom.xml内容开始" );
+        String pomXmlPath = projectPath + "/" + GeneratorConstant.POM_XML;
+        File pomXmlFile = new File(pomXmlPath);
+        replacePomXml(pomXmlFile, targetPackageName, targetArtifactId);
+        log.info("替换pom.xml内容结束" );
+
+        // 替换application.yml内容
+        log.info("替换application.yml内容开始" );
+        String applicationYmlPath = projectPath + "/" + GeneratorConstant.APPLICATION_YML;
+        File applicationYmlFile = new File(applicationYmlPath);
+        replaceApplicationYml(applicationYmlFile, targetArtifactId);
+        log.info("替换application.yml内容结束" );
+
+        // 替换xml中的包名称
+        log.info("替换xml中的包名称开始" );
+        String mapperDir = projectPath + GeneratorConstant.SRC_MAIN_RESOURCES_MAPPER;
+        File mapperXmlParentFile = new File(mapperDir);
+        recursionReplacePackageName(mapperXmlParentFile, currentPackageName, targetPackageName, GeneratorConstant.DOT_XML);
+        log.info("替换xml中的包名称结束" );
+
+        // 重命名main包名称
+        log.info("重命名main包名称开始" );
+        File targetMainParentFile = renameMainJavaPackage(projectPath, currentPackagePath, targetPackagePath);
+        log.info("重命名main包名称结束" );
+        recursionReplacePackageName(targetMainParentFile, currentPackageName, targetPackageName, GeneratorConstant.DOT_JAVA);
+
+        // 重命名test包名称
+        File targetTestParentFile = renameTestJavaPackage(projectPath, currentPackagePath, targetPackagePath);
+        recursionReplacePackageName(targetTestParentFile, currentPackageName, targetPackageName, GeneratorConstant.DOT_JAVA);
+
+    }
+
+    /**
+     * 重命名src/main/java下的包名称
+     *
+     * @param projectPath
+     * @param currentPackagePath
+     * @param targetPackagePath
+     * @return
+     * @throws Exception
+     */
+    public static File renameMainJavaPackage(String projectPath, String currentPackagePath, String targetPackagePath) throws Exception {
+        String srcParentPackage = projectPath + GeneratorConstant.SRC_MAIN_JAVA + currentPackagePath;
+        String targetSrcParentPackage = projectPath + GeneratorConstant.SRC_MAIN_JAVA + targetPackagePath;
+        return renameJavaPackage(srcParentPackage, targetSrcParentPackage);
+    }
+
+    /**
+     * 重命名src/test/java下的包名称
+     *
+     * @param projectPath
+     * @param currentPackagePath
+     * @param targetPackagePath
+     * @return
+     * @throws Exception
+     */
+    public static File renameTestJavaPackage(String projectPath, String currentPackagePath, String targetPackagePath) throws Exception {
+        String srcParentPackage = projectPath + GeneratorConstant.SRC_TEST_JAVA + currentPackagePath;
+        String targetSrcParentPackage = projectPath + GeneratorConstant.SRC_TEST_JAVA + targetPackagePath;
+        return renameJavaPackage(srcParentPackage, targetSrcParentPackage);
+    }
+
+    /**
+     * 重命名java包名称
+     *
+     * @param srcParentPackage
+     * @param targetSrcParentPackage
+     * @return
+     * @throws Exception
+     */
+    public static File renameJavaPackage(String srcParentPackage, String targetSrcParentPackage) throws Exception {
+        File srcParentFile = new File(srcParentPackage);
+        File targetSrcParentFile = new File(targetSrcParentPackage);
+        log.info("srcParentFile:" + srcParentFile);
+        log.info("targetSrcParentFile:" + targetSrcParentFile);
+        if (!targetSrcParentFile.exists()) {
+            log.info("创建目录成功：" + targetSrcParentFile);
+            targetSrcParentFile.mkdirs();
+        }
+        boolean flag = srcParentFile.renameTo(targetSrcParentFile);
+        if (flag) {
+            log.info("重命名目录成功：" + targetSrcParentFile);
+        } else {
+            throw new RuntimeException("目录替换失败" );
+        }
+        return targetSrcParentFile;
+    }
+
+
+    /**
+     * 替换源代码中的包路径
+     *
+     * @param targetFile
+     */
+    public static void recursionReplacePackageName(File targetFile, String currentPackageName, String targetPackageName, String fileSuffix) throws Exception {
+        if (targetFile == null) {
+            return;
+        }
+        if (!targetFile.exists()) {
+            return;
+        }
+        boolean directory = targetFile.isDirectory();
+        // 如果是目录，则继续递归
+        if (directory) {
+            File[] files = targetFile.listFiles();
+            if (ArrayUtils.isNotEmpty(files)) {
+                for (File file : files) {
+                    recursionReplacePackageName(file, currentPackageName, targetPackageName, fileSuffix);
+                }
+            }
+        } else {
+            // 判断是否是.java的文件，是则操作，否则跳过
+            String name = targetFile.getName();
+            if (name.endsWith(fileSuffix)) {
+                // 如果是指定文件，则读取文件中的内容，进行替换
+                replaceAllPackageName(targetFile, currentPackageName, targetPackageName);
+                log.info("替换目标文件包名称：" + targetFile);
+            }
+        }
+
+    }
+
+    /**
+     * 替换文件内容
+     *
+     * @param file
+     * @param currentPackageName
+     * @param targetPackageName
+     * @throws Exception
+     */
+    public static void replaceAllPackageName(File file, String currentPackageName, String targetPackageName) throws Exception {
+        String content = FileUtils.readFileToString(file, GeneratorConstant.UTF8);
+        if (StringUtils.isNotBlank(content)) {
+            content = content.replaceAll(currentPackageName, targetPackageName);
+            // 重写文件内容
+            FileUtils.writeStringToFile(file, content, GeneratorConstant.UTF8, false);
+        }
+    }
+
+    /**
+     * 替换pom.xml内容
+     *
+     * @param file
+     * @param targetPackageName
+     * @param targetArtifactId
+     * @throws Exception
+     */
+    public static void replacePomXml(File file, String targetPackageName, String targetArtifactId) throws Exception {
+        List<String> lines = FileUtils.readLines(file, GeneratorConstant.UTF8);
+        if (CollectionUtils.isNotEmpty(lines)) {
+            String groupIdElement = "\t<groupId>" + targetPackageName + "</groupId>";
+            String artifactIdElement = "\t<artifactId>" + targetArtifactId + "</artifactId>";
+            String nameElement = "\t<name>" + targetArtifactId + "</name>";
+            lines.set(11, groupIdElement);
+            lines.set(12, artifactIdElement);
+            lines.set(15, nameElement);
+            // 重写文件内容
+            FileUtils.writeLines(file, GeneratorConstant.UTF8, lines, System.lineSeparator(), false);
+            log.info("pom.xml文件内容替换成功" );
+        }
+    }
+
+    /**
+     * 替换application.yml内容
+     *
+     * @param file
+     * @param targetArtifactId
+     * @throws Exception
+     */
+    public static void replaceApplicationYml(File file, String targetArtifactId) throws Exception {
+        List<String> lines = FileUtils.readLines(file, GeneratorConstant.UTF8);
+        List<String> targetLines = new ArrayList<>();
+        if (CollectionUtils.isNotEmpty(lines)) {
+            for (String line : lines) {
+                String trimLine = line.trim();
+                if (trimLine.startsWith("projectPrefix" )) {
+                    line = "    projectPrefix: " + targetArtifactId;
+                }
+                targetLines.add(line);
+            }
+            // 重写文件内容
+            FileUtils.writeLines(file, GeneratorConstant.UTF8, targetLines, System.lineSeparator(), false);
+            log.info("pom.xml文件内容替换成功" );
+        }
+    }
+
+}
diff --git a/src/test/java/io/geekidea/boot/generator/handler/SimpleGeneratorHandler.java b/src/test/java/io/geekidea/boot/generator/handler/SimpleGeneratorHandler.java
new file mode 100644
index 00000000..1e84635f
--- /dev/null
+++ b/src/test/java/io/geekidea/boot/generator/handler/SimpleGeneratorHandler.java
@@ -0,0 +1,190 @@
+package io.geekidea.boot.generator.handler;
+
+import cn.hutool.core.date.DateUtil;
+import io.geekidea.boot.common.constant.CommonConstant;
+import io.geekidea.boot.framework.exception.BusinessException;
+import io.geekidea.boot.generator.config.SimpleGeneratorConfig;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author geekidea
+ * @date 2022/7/2
+ **/
+@Slf4j
+public class SimpleGeneratorHandler {
+    private static final String PACKAGE_NAME = CommonConstant.PACKAGE_NAME;
+    private static final String CONTROLLER_PACKAGE_NAME = ".controller";
+    private static final String SERVICE_PACKAGE_NAME = ".service";
+    private static final String SERVICE_IMPL_PACKAGE_NAME = ".service.impl";
+    private static final String MAPPER_PACKAGE_NAME = ".mapper";
+    private static final String CONTROLLER_SUFFIX = "Controller";
+    private static final String SERVICE_SUFFIX = "Service";
+    private static final String SERVICE_IMPL_SUFFIX = "ServiceImpl";
+    private static final String MAPPER_SUFFIX = "Mapper";
+    private static final String TEMPLATE_DIR = "templates" + File.separator + "simple";
+    private static final String CONTROLLER_TEMPLATE_NAME = "simpleController.java.vm";
+    private static final String SERVICE_TEMPLATE_NAME = "simpleService.java.vm";
+    private static final String SERVICE_IMPL_TEMPLATE_NAME = "simpleServiceImpl.java.vm";
+    private static final String MAPPER_TEMPLATE_NAME = "simpleMapper.java.vm";
+    private static final String MAPPER_XML_TEMPLATE_NAME = "simpleMapper.xml.vm";
+    private static final String DOT_JAVA = ".java";
+    private static final String DOT_XML = ".xml";
+    private static final String USER_DIR = "user.dir";
+    private static final String JAVA_DIR = File.separator + "src" + File.separator + "main" + File.separator + "java" + File.separator;
+    private static final String RESOURCES_DIR = File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator;
+    private static final String TEST_RESOURCES_DIR = File.separator + "src" + File.separator + "test" + File.separator + "resources" + File.separator;
+    private static final String MAPPER_XML_DIR = RESOURCES_DIR + File.separator + "mapper" + File.separator;
+
+    public static void generateSimple(SimpleGeneratorConfig config) throws Exception {
+        if (config == null) {
+            throw new BusinessException("配置不能为空");
+        }
+        boolean generateController = config.isGenerateController();
+        boolean generateService = config.isGenerateService();
+        boolean generateMapper = config.isGenerateMapper();
+        if (!generateController && !generateService && !generateMapper) {
+            log.warn("未设置需要要生成的内容");
+            return;
+        }
+        String name = config.getName();
+        String desc = config.getDesc();
+        String author = config.getAuthor();
+        String moduleName = config.getModuleName();
+        if (StringUtils.isBlank(name)) {
+            throw new BusinessException("名称不能为空");
+        }
+        String camelName = name.substring(0, 1).toLowerCase() + name.substring(1);
+        String projectDir = System.getProperty(USER_DIR);
+        String templateDir = projectDir + TEST_RESOURCES_DIR + TEMPLATE_DIR;
+        Properties p = new Properties();
+        // 设置模板加载路径 为D盘 work文件夹
+        p.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, templateDir);
+        Velocity.init(p);
+        // controller
+        String controllerName = name + CONTROLLER_SUFFIX;
+        String controllerPackage;
+        if (StringUtils.isNotBlank(moduleName)) {
+            controllerPackage = PACKAGE_NAME + "." + moduleName + CONTROLLER_PACKAGE_NAME;
+        } else {
+            controllerPackage = PACKAGE_NAME + CONTROLLER_PACKAGE_NAME;
+        }
+        String controllerPackagePath = controllerPackage.replaceAll("\\.", "/");
+        String controllerOutFileDir = projectDir + JAVA_DIR + controllerPackagePath;
+        String controllerOutFileName = controllerName + DOT_JAVA;
+        // service
+        String serviceName = name + SERVICE_SUFFIX;
+        String serviceCamelName = serviceName.substring(0, 1).toLowerCase() + serviceName.substring(1);
+        String servicePackage;
+        if (StringUtils.isNotBlank(moduleName)) {
+            servicePackage = PACKAGE_NAME + "." + moduleName + SERVICE_PACKAGE_NAME;
+        } else {
+            servicePackage = PACKAGE_NAME + SERVICE_PACKAGE_NAME;
+        }
+        String servicePackagePath = servicePackage.replaceAll("\\.", "/");
+        String serviceOutFileDir = projectDir + JAVA_DIR + servicePackagePath;
+        String serviceOutFileName = serviceName + DOT_JAVA;
+        // serviceImpl
+        String serviceImplName = name + SERVICE_IMPL_SUFFIX;
+        String serviceImplPackage;
+        if (StringUtils.isNotBlank(moduleName)) {
+            serviceImplPackage = PACKAGE_NAME + "." + moduleName + SERVICE_IMPL_PACKAGE_NAME;
+        } else {
+            serviceImplPackage = PACKAGE_NAME + SERVICE_IMPL_PACKAGE_NAME;
+        }
+        String serviceImplPackagePath = serviceImplPackage.replaceAll("\\.", "/");
+        String serviceImplOutFileDir = projectDir + JAVA_DIR + serviceImplPackagePath;
+        String serviceImplOutFileName = serviceImplName + DOT_JAVA;
+        // mapper
+        String mapperName = name + MAPPER_SUFFIX;
+        String mapperCamelName = mapperName.substring(0, 1).toLowerCase() + mapperName.substring(1);
+        String mapperPackage;
+        if (StringUtils.isNotBlank(moduleName)) {
+            mapperPackage = PACKAGE_NAME + "." + moduleName + MAPPER_PACKAGE_NAME;
+        } else {
+            mapperPackage = PACKAGE_NAME + MAPPER_PACKAGE_NAME;
+        }
+        String mapperPackagePath = mapperPackage.replaceAll("\\.", "/");
+        String mapperOutFileDir = projectDir + JAVA_DIR + mapperPackagePath;
+        String mapperOutFileName = mapperName + DOT_JAVA;
+        // mapper xml
+        String mapperXmlOutFileDir;
+        if (StringUtils.isNotBlank(moduleName)) {
+            mapperXmlOutFileDir = projectDir + MAPPER_XML_DIR + moduleName + File.separator;
+        } else {
+            mapperXmlOutFileDir = projectDir + MAPPER_XML_DIR;
+        }
+        String mapperXmlOutFileName = mapperName + DOT_XML;
+        Map<String, Object> params = new HashMap<>();
+        params.put("name", name);
+        params.put("camelName", camelName);
+        params.put("desc", desc);
+        params.put("author", author);
+        params.put("createDate", DateUtil.formatDate(new Date()));
+        params.put("controllerName", controllerName);
+        params.put("controllerPackage", controllerPackage);
+        params.put("serviceName", serviceName);
+        params.put("serviceCamelName", serviceCamelName);
+        params.put("servicePackage", servicePackage);
+        params.put("serviceImplName", serviceImplName);
+        params.put("serviceImplPackage", serviceImplPackage);
+        params.put("mapperName", mapperName);
+        params.put("mapperCamelName", mapperCamelName);
+        params.put("mapperPackage", mapperPackage);
+        params.put("generateController", generateController);
+        params.put("generateService", generateService);
+        params.put("generateMapper", generateMapper);
+        if (generateController) {
+            render(CONTROLLER_TEMPLATE_NAME, params, controllerOutFileDir, controllerOutFileName);
+        }
+        if (generateService) {
+            render(SERVICE_TEMPLATE_NAME, params, serviceOutFileDir, serviceOutFileName);
+            render(SERVICE_IMPL_TEMPLATE_NAME, params, serviceImplOutFileDir, serviceImplOutFileName);
+        }
+        if (generateMapper) {
+            render(MAPPER_TEMPLATE_NAME, params, mapperOutFileDir, mapperOutFileName);
+            render(MAPPER_XML_TEMPLATE_NAME, params, mapperXmlOutFileDir, mapperXmlOutFileName);
+        }
+    }
+
+    public static void render(String templateName, Map<String, Object> params, String outFileDirPath, String outFileName) throws Exception {
+        if (StringUtils.isBlank(templateName)) {
+            throw new BusinessException("模板名称不能为空");
+        }
+        if (MapUtils.isEmpty(params)) {
+            throw new BusinessException("模板参数不能为空");
+        }
+        if (StringUtils.isBlank(outFileDirPath)) {
+            throw new BusinessException("文件输出目录路径不能为空");
+        }
+        if (StringUtils.isBlank(outFileName)) {
+            throw new BusinessException("文件输出名称不能为空");
+        }
+        File outFieDir = new File(outFileDirPath);
+        if (!outFieDir.exists()) {
+            outFieDir.mkdirs();
+        }
+        File outFile = new File(outFieDir, outFileName);
+        VelocityContext context = new VelocityContext(params);
+        Template template = Velocity.getTemplate(templateName, "UTF-8");
+        Writer writer = new PrintWriter(new FileOutputStream(outFile));
+        template.merge(context, writer);
+        writer.flush();
+        writer.close();
+    }
+
+}
diff --git a/src/test/java/io/geekidea/boot/generator/jdbc/JdbcUtil.java b/src/test/java/io/geekidea/boot/generator/jdbc/JdbcUtil.java
new file mode 100644
index 00000000..6f71cd34
--- /dev/null
+++ b/src/test/java/io/geekidea/boot/generator/jdbc/JdbcUtil.java
@@ -0,0 +1,224 @@
+package io.geekidea.boot.generator.jdbc;
+
+import cn.hutool.core.lang.Dict;
+import cn.hutool.setting.yaml.YamlUtil;
+import io.geekidea.boot.generator.exception.GeneratorException;
+import io.geekidea.boot.generator.vo.GeneratorColumnDbVo;
+import io.geekidea.boot.generator.vo.GeneratorTableDbVo;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.*;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author geekidea
+ * @date 2024/1/15
+ **/
+@Slf4j
+public class JdbcUtil {
+
+    private static String driver;
+    private static String url;
+    private static String username;
+    private static String password;
+
+    static {
+        // 加载jdbc配置
+        loadJdbcConfig();
+        try {
+            // 加载驱动
+            Class.forName(driver);
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 加载jdbc配置信息
+     */
+    public static void loadJdbcConfig() {
+        log.info("生成代码JDBC连接信息：");
+        String projectPath = System.getProperty("user.dir");
+        String applicationYmlPath = "/src/main/resources/application.yml";
+        String applicationYmlFilePath = projectPath + applicationYmlPath;
+        Dict applicationYmlDict = YamlUtil.loadByPath(applicationYmlFilePath);
+        // 从application.yml中获取spring.profiles.active的环境值，如：dev、test、prod等
+        String active = applicationYmlDict.getByPath("spring.profiles.active", String.class);
+        log.info("active = " + active);
+        // 从application.yml获取driver-class-name
+        driver = applicationYmlDict.getByPath("spring.datasource.driver-class-name", String.class);
+        // 获取指定环境的配置文件
+        String applicationActiveYmlPath = "/src/main/resources/application-" + active + ".yml";
+        String applicationActiveYmlFilePath = projectPath + applicationActiveYmlPath;
+        Dict applicationActiveYmlDict = YamlUtil.loadByPath(applicationActiveYmlFilePath);
+        // 读取url、username、password
+        url = applicationActiveYmlDict.getByPath("spring.datasource.url", String.class);
+        username = applicationActiveYmlDict.getByPath("spring.datasource.username", String.class);
+        password = applicationActiveYmlDict.getByPath("spring.datasource.password", String.class);
+        log.info("activeFile：" + applicationActiveYmlFilePath);
+        log.info("driver: " + driver);
+        log.info("url: " + url);
+        log.info("username: " + username);
+        log.info("password: " + password);
+        if (StringUtils.isBlank(driver) ||
+                StringUtils.isBlank(url) ||
+                StringUtils.isBlank(username) ||
+                StringUtils.isBlank(password)
+        ) {
+            throw new GeneratorException("JDBC配置异常");
+        }
+    }
+
+    /**
+     * 获取数据库连接
+     *
+     * @return
+     */
+    public static Connection getConnection() {
+        try {
+            Connection connection = DriverManager.getConnection(url, username, password);
+            return connection;
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 获取数据库原始表信息
+     *
+     * @param tableName
+     * @return
+     */
+    public static GeneratorTableDbVo getGeneratorTableDbVo(String tableName) {
+        Connection connection = null;
+        PreparedStatement prepareStatement = null;
+        ResultSet resultSet = null;
+        try {
+            connection = getConnection();
+            String sql = "select TABLE_NAME, TABLE_COMMENT, CREATE_TIME\n" +
+                    "        from information_schema.TABLES\n" +
+                    "        where TABLE_SCHEMA = (select database())\n" +
+                    "          and TABLE_NAME not like 'generator_%'\n" +
+                    "          and TABLE_NAME = ?";
+            log.info("get tableInfo sql:" + sql);
+            prepareStatement = connection.prepareStatement(sql);
+            log.info("tableName:" + tableName);
+            prepareStatement.setString(1, tableName);
+            resultSet = prepareStatement.executeQuery();
+            if (resultSet.next()) {
+                String dbTableName = resultSet.getString("TABLE_NAME");
+                String tableComment = resultSet.getString("TABLE_COMMENT");
+                Date createTime = resultSet.getDate("CREATE_TIME");
+                GeneratorTableDbVo generatorTableDbVo = new GeneratorTableDbVo();
+                generatorTableDbVo.setTableName(dbTableName);
+                generatorTableDbVo.setTableComment(tableComment);
+                generatorTableDbVo.setCreateTime(createTime);
+                return generatorTableDbVo;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            close(connection, prepareStatement, resultSet);
+        }
+        return null;
+    }
+
+    /**
+     * 获取数据库原始表信息
+     *
+     * @param tableName
+     * @return
+     */
+    public static List<GeneratorColumnDbVo> getGeneratorColumnDbVos(String tableName) {
+        Connection connection = null;
+        PreparedStatement prepareStatement = null;
+        ResultSet resultSet = null;
+        try {
+            connection = getConnection();
+            String sql = "select TABLE_NAME,\n" +
+                    "               COLUMN_NAME,\n" +
+                    "               COLUMN_COMMENT,\n" +
+                    "               DATA_TYPE,\n" +
+                    "               COLUMN_TYPE,\n" +
+                    "               ORDINAL_POSITION                 columnSort,\n" +
+                    "               if(COLUMN_KEY = 'PRI', 1, 0)     isPk,\n" +
+                    "               if(IS_NULLABLE = 'NO', 1, 0)     isRequired,\n" +
+                    "               if(COLUMN_DEFAULT is null, 0, 1) isDefaultValue\n" +
+                    "        from information_schema.COLUMNS\n" +
+                    "        where TABLE_SCHEMA = (select database())\n" +
+                    "          and TABLE_NAME = ?\n" +
+                    "        order by ORDINAL_POSITION";
+            log.info("get columnInfo sql:" + sql);
+            prepareStatement = connection.prepareStatement(sql);
+            log.info("tableName:" + tableName);
+            prepareStatement.setString(1, tableName);
+            resultSet = prepareStatement.executeQuery();
+            List<GeneratorColumnDbVo> generatorColumnDbVos = new ArrayList<>();
+            while (resultSet.next()) {
+                String dbTableName = resultSet.getString("TABLE_NAME");
+                String columnName = resultSet.getString("COLUMN_NAME");
+                String columnComment = resultSet.getString("COLUMN_COMMENT");
+                String dataType = resultSet.getString("DATA_TYPE");
+                String columnType = resultSet.getString("COLUMN_TYPE");
+                Integer columnSort = resultSet.getInt("columnSort");
+                Boolean isPk = resultSet.getBoolean("isPk");
+                Boolean isRequired = resultSet.getBoolean("isRequired");
+                Boolean isDefaultValue = resultSet.getBoolean("isDefaultValue");
+                GeneratorColumnDbVo generatorColumnDbVo = new GeneratorColumnDbVo();
+                generatorColumnDbVo.setTableName(dbTableName);
+                generatorColumnDbVo.setColumnName(columnName);
+                generatorColumnDbVo.setColumnComment(columnComment);
+                generatorColumnDbVo.setDataType(dataType);
+                generatorColumnDbVo.setColumnType(columnType);
+                generatorColumnDbVo.setColumnSort(columnSort);
+                generatorColumnDbVo.setIsPk(isPk);
+                generatorColumnDbVo.setIsRequired(isRequired);
+                generatorColumnDbVo.setIsDefaultValue(isDefaultValue);
+                generatorColumnDbVos.add(generatorColumnDbVo);
+                System.out.println("generatorColumnDbVo = " + generatorColumnDbVo);
+            }
+            return generatorColumnDbVos;
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            close(connection, prepareStatement, resultSet);
+        }
+        return null;
+    }
+
+    /**
+     * 释放资源
+     *
+     * @param connection
+     * @param statement
+     * @param resultSet
+     */
+    public static void close(Connection connection, Statement statement, ResultSet resultSet) {
+        if (resultSet != null) {
+            try {
+                resultSet.close();
+            } catch (SQLException e) {
+                e.printStackTrace();
+            }
+        }
+        if (statement != null) {
+            try {
+                statement.close();
+            } catch (SQLException e) {
+                e.printStackTrace();
+            }
+        }
+        if (connection != null) {
+            try {
+                connection.close();
+            } catch (SQLException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+}
diff --git a/src/test/java/io/geekidea/boot/generator/rename/RenameExecutor.java b/src/test/java/io/geekidea/boot/generator/rename/RenameExecutor.java
new file mode 100644
index 00000000..1c229aa2
--- /dev/null
+++ b/src/test/java/io/geekidea/boot/generator/rename/RenameExecutor.java
@@ -0,0 +1,23 @@
+package io.geekidea.boot.generator.rename;
+
+import io.geekidea.boot.generator.handler.RenameHandler;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @author geekidea
+ * @date 2023/11/24
+ **/
+@Slf4j
+public class RenameExecutor {
+
+    public static void main(String[] args) throws Exception {
+        // 当前项目包名称
+        String currentPackageName = "io.geekidea.boot";
+        // 目标项目包名称
+        String targetPackageName = "io.geekidea.boot";
+        // artifactId名称
+        String targetArtifactId = "spring-boot-plus";
+        RenameHandler.execute(currentPackageName, targetPackageName, targetArtifactId);
+    }
+
+}
diff --git a/src/test/resources/templates/simple/simpleController.java.vm b/src/test/resources/templates/simple/simpleController.java.vm
new file mode 100644
index 00000000..158d5534
--- /dev/null
+++ b/src/test/resources/templates/simple/simpleController.java.vm
@@ -0,0 +1,29 @@
+package $!{controllerPackage};
+
+#if($!{generateService})
+import $!{servicePackage}.$!{serviceName};
+import org.springframework.beans.factory.annotation.Autowired;
+#end
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * $!{desc}控制器
+ *
+ * @author $!{author}
+ * @date $!{createDate}
+ **/
+@Slf4j
+@RestController
+@RequestMapping("/$!{camelName}")
+@Tag(name = "$!{desc}")
+public class $!{controllerName} {
+
+#if($!{generateService})
+@Autowired
+private $!{serviceName} $!{serviceCamelName};
+#end
+
+}
diff --git a/src/test/resources/templates/simple/simpleMapper.java.vm b/src/test/resources/templates/simple/simpleMapper.java.vm
new file mode 100644
index 00000000..8887aa4f
--- /dev/null
+++ b/src/test/resources/templates/simple/simpleMapper.java.vm
@@ -0,0 +1,14 @@
+package $!{mapperPackage};
+
+import org.springframework.stereotype.Repository;
+
+/**
+ * $!{desc}Mapper
+ * @author $!{author}
+ * @date $!{createDate}
+ **/
+@Repository
+public interface $!{mapperName} {
+
+
+}
diff --git a/src/test/resources/templates/simple/simpleMapper.xml.vm b/src/test/resources/templates/simple/simpleMapper.xml.vm
new file mode 100644
index 00000000..905a8c5a
--- /dev/null
+++ b/src/test/resources/templates/simple/simpleMapper.xml.vm
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="$!{mapperPackage}.$!{mapperName}">
+
+</mapper>
diff --git a/src/test/resources/templates/simple/simpleService.java.vm b/src/test/resources/templates/simple/simpleService.java.vm
new file mode 100644
index 00000000..cfe24cb4
--- /dev/null
+++ b/src/test/resources/templates/simple/simpleService.java.vm
@@ -0,0 +1,10 @@
+package $!{servicePackage};
+
+/**
+ * $!{desc}服务接口
+ * @author $!{author}
+ * @date $!{createDate}
+ **/
+public interface $!{serviceName} {
+
+}
diff --git a/src/test/resources/templates/simple/simpleServiceImpl.java.vm b/src/test/resources/templates/simple/simpleServiceImpl.java.vm
new file mode 100644
index 00000000..b8234d06
--- /dev/null
+++ b/src/test/resources/templates/simple/simpleServiceImpl.java.vm
@@ -0,0 +1,26 @@
+package $!{serviceImplPackage};
+
+#if($!{generateMapper})
+import $!{mapperPackage}.$!{mapperName};
+import org.springframework.beans.factory.annotation.Autowired;
+#end
+import $!{servicePackage}.$!{serviceName};
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+/**
+ * $!{desc}服务接口实现
+ *
+ * @author $!{author}
+ * @date $!{createDate}
+ **/
+@Slf4j
+@Service
+public class $!{serviceImplName} implements $!{serviceName} {
+
+#if($!{generateMapper})
+@Autowired
+private $!{mapperName} $!{mapperCamelName};
+#end
+
+}
diff --git a/system/.gitignore b/system/.gitignore
deleted file mode 100644
index ccdf702f..00000000
--- a/system/.gitignore
+++ /dev/null
@@ -1,36 +0,0 @@
-/target/
-/classes
-!.mvn/wrapper/maven-wrapper.jar
-
-### STS ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-
-### IntelliJ IDEA ###
-.idea
-*.iws
-*.iml
-*.ipr
-
-### NetBeans ###
-/nbproject/private/
-/build/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-
-.DS_Store
-
-*.log
-logs
-
-*.rdb
-
-
-
diff --git a/system/README.md b/system/README.md
deleted file mode 100644
index 795f7580..00000000
--- a/system/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# system 项目系统模块
\ No newline at end of file
diff --git a/system/pom.xml b/system/pom.xml
deleted file mode 100644
index 230daf25..00000000
--- a/system/pom.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <groupId>io.geekidea.springbootplus</groupId>
-        <artifactId>parent</artifactId>
-        <version>2.0</version>
-    </parent>
-
-    <artifactId>system</artifactId>
-    <name>system</name>
-    <description>系统模块</description>
-
-    <dependencies>
-        <dependency>
-            <groupId>io.geekidea.springbootplus</groupId>
-            <artifactId>framework</artifactId>
-        </dependency>
-    </dependencies>
-
-</project>
\ No newline at end of file
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/controller/DownloadController.java b/system/src/main/java/io/geekidea/springbootplus/system/controller/DownloadController.java
deleted file mode 100644
index 5a7b8ed4..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/controller/DownloadController.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.controller;
-
-import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties;
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-import io.geekidea.springbootplus.framework.util.DownloadUtil;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-
-import javax.servlet.http.HttpServletResponse;
-import java.util.List;
-
-/**
- * 下载控制器
- *
- * @author geekidea
- * @date 2019/8/20
- * @since 1.2.1-RELEASE
- */
-@Slf4j
-@Controller
-@RequestMapping("/download")
-@Module("system")
-@Api(value = "文件下载", tags = {"文件下载"})
-public class DownloadController {
-
-    @Autowired
-    private SpringBootPlusProperties springBootPlusProperties;
-
-    /**
-     * 下载文件
-     */
-    @GetMapping("/{downloadFileName}")
-    @OperationLog(name = "下载文件", type = OperationLogType.download)
-    @ApiOperation(value = "下载文件", notes = "下载文件", response = ApiResult.class)
-    public void download(@PathVariable(required = true) String downloadFileName, HttpServletResponse response) throws Exception {
-        // 下载目录，既是上传目录
-        String downloadDir = springBootPlusProperties.getUploadPath();
-        // 允许下载的文件后缀
-        List<String> allowFileExtensions = springBootPlusProperties.getAllowDownloadFileExtensions();
-        // 文件下载，使用默认下载处理器
-        // 文件下载，使用自定义下载处理器
-        DownloadUtil.download(downloadDir, downloadFileName, allowFileExtensions, response, (dir, fileName, file, fileExtension, contentType, length) -> {
-            // 下载自定义处理，返回true：执行下载，false：取消下载
-            log.info("dir = " + dir);
-            log.info("fileName = " + fileName);
-            log.info("file = " + file);
-            log.info("fileExtension = " + fileExtension);
-            log.info("contentType = " + contentType);
-            log.info("length = " + length);
-            return true;
-        });
-    }
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/controller/HelloWorldController.java b/system/src/main/java/io/geekidea/springbootplus/system/controller/HelloWorldController.java
deleted file mode 100644
index fd8af5ba..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/controller/HelloWorldController.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.controller;
-
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.io.IOException;
-
-/**
- * Hello World Controller
- *
- * @author geekidea
- * @date 2019-10-09
- **/
-@Slf4j
-@Api(value = "Hello World", tags = {"Hello World"})
-@RestController
-@RequestMapping("/hello")
-public class HelloWorldController {
-
-    /**
-     * Hello World
-     *
-     * @return
-     * @throws IOException
-     */
-    @GetMapping(value = "/world")
-    @OperationLog(name = "helloWorld")
-    @ApiOperation(value = "Hello World", response = String.class)
-    public ApiResult<String> helloWorld() throws IOException {
-        log.debug("Hello World...");
-        return ApiResult.ok("Hello World");
-    }
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/controller/LoginController.java b/system/src/main/java/io/geekidea/springbootplus/system/controller/LoginController.java
deleted file mode 100644
index 3e131a60..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/controller/LoginController.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.controller;
-
-import com.alibaba.fastjson.JSON;
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLogIgnore;
-import io.geekidea.springbootplus.framework.shiro.util.JwtTokenUtil;
-import io.geekidea.springbootplus.system.service.LoginService;
-import io.geekidea.springbootplus.system.service.SysUserService;
-import io.geekidea.springbootplus.system.vo.LoginSysUserTokenVo;
-import io.geekidea.springbootplus.system.vo.SysUserQueryVo;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * 登录控制器
- *
- * @author geekidea
- * @date 2019-09-28
- * @since 1.3.0.RELEASE
- **/
-@Slf4j
-@RestController
-@Module("system")
-@Api(value = "系统登录API", tags = {"系统登录"})
-public class LoginController {
-
-    @Autowired
-    private LoginService loginService;
-
-    @Autowired
-    private SysUserService sysUserService;
-
-    @Autowired
-    private RedisTemplate redisTemplate;
-
-    @PostMapping("/login")
-    @OperationLogIgnore
-    @ApiOperation(value = "登录", notes = "系统用户登录", response = io.geekidea.springbootplus.system.vo.LoginSysUserTokenVo.class)
-    public ApiResult<LoginSysUserTokenVo> login(@Validated @RequestBody io.geekidea.springbootplus.system.param.LoginParam loginParam, HttpServletResponse response) throws Exception {
-        LoginSysUserTokenVo loginSysUserTokenVo = loginService.login(loginParam);
-        // 设置token响应头
-        response.setHeader(JwtTokenUtil.getTokenName(), loginSysUserTokenVo.getToken());
-        return ApiResult.ok(loginSysUserTokenVo, "登录成功");
-    }
-
-
-    /**
-     * 根据token获取系统登录用户信息
-     *
-     * @return
-     */
-    @GetMapping("/getSysUserInfo")
-    @ApiOperation(value = "根据token获取系统登录用户信息", response = SysUserQueryVo.class)
-    public ApiResult<JSON> getSysUser() throws Exception {
-//        String token =  JwtTokenUtil.getToken();
-//        String tokenSha256 = DigestUtils.sha256Hex(token);
-//        LoginSysUserVo loginSysUserVo = (LoginSysUserVo) redisTemplate.opsForValue().get(tokenSha256);
-//        return ApiResult.ok(loginSysUserVo);
-
-        String json = "{\n" +
-                "    roles: ['admin'],\n" +
-                "    introduction: 'I am a super administrator',\n" +
-                "    avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',\n" +
-                "    name: 'Super Admin'\n" +
-                "  }";
-        JSON array = JSON.parseObject(json);
-
-        return ApiResult.ok(array);
-    }
-
-    @PostMapping("/logout")
-    @OperationLogIgnore
-    public ApiResult<String> logout(HttpServletRequest request) throws Exception {
-        loginService.logout(request);
-        return ApiResult.ok("退出成功");
-    }
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/controller/ResourceController.java b/system/src/main/java/io/geekidea/springbootplus/system/controller/ResourceController.java
deleted file mode 100644
index f9be29a3..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/controller/ResourceController.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.controller;
-
-import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties;
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * 图片等文件资源访问控制器
- * /api/resource 访问路径 用于区分 文件访问虚拟目录映射 /resource
- *
- * @author geekidea
- * @date 2019/8/20
- * @since 1.2.1-RELEASE
- */
-@Slf4j
-@Controller
-@RequestMapping("/api/resource")
-@Module("system")
-@Api(value = "资源访问", tags = {"资源访问"})
-public class ResourceController {
-
-    @Autowired
-    private SpringBootPlusProperties springBootPlusProperties;
-
-    /**
-     * 访问资源
-     */
-    @GetMapping("/image/{imageFileName}")
-    @OperationLog(name = "访问资源", type = OperationLogType.ADD)
-    @ApiOperation(value = "访问资源", response = ApiResult.class)
-    public void getImage(@PathVariable(required = true) String imageFileName, HttpServletResponse response) throws Exception {
-        log.info("imageFileName:{}", imageFileName);
-        // 重定向到图片访问路径
-        response.sendRedirect(springBootPlusProperties.getResourceAccessPath() + imageFileName);
-    }
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/controller/SysDepartmentController.java b/system/src/main/java/io/geekidea/springbootplus/system/controller/SysDepartmentController.java
deleted file mode 100644
index 0566feab..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/controller/SysDepartmentController.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.controller;
-
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.common.controller.BaseController;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-import io.geekidea.springbootplus.system.entity.SysDepartment;
-import io.geekidea.springbootplus.system.param.SysDepartmentPageParam;
-import io.geekidea.springbootplus.system.service.SysDepartmentService;
-import io.geekidea.springbootplus.system.vo.SysDepartmentQueryVo;
-import io.geekidea.springbootplus.system.vo.SysDepartmentTreeVo;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * <pre>
- * 部门 前端控制器
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Slf4j
-@RestController
-@RequestMapping("/sysDepartment")
-@Module("system")
-@Api(value = "系统部门API", tags = {"系统部门"})
-public class SysDepartmentController extends BaseController {
-
-    @Autowired
-    private SysDepartmentService sysDepartmentService;
-
-    /**
-     * 添加部门
-     */
-    @PostMapping("/add")
-    @RequiresPermissions("sys:department:add")
-    @OperationLog(name = "添加部门", type = OperationLogType.ADD)
-    @ApiOperation(value = "添加部门", response = ApiResult.class)
-    public ApiResult<Boolean> addSysDepartment(@Validated @RequestBody SysDepartment sysDepartment) throws Exception {
-        boolean flag = sysDepartmentService.saveSysDepartment(sysDepartment);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 修改部门
-     */
-    @PostMapping("/update")
-    @RequiresPermissions("sys:department:update")
-    @OperationLog(name = "修改部门", type = OperationLogType.UPDATE)
-    @ApiOperation(value = "修改部门", response = ApiResult.class)
-    public ApiResult<Boolean> updateSysDepartment(@Validated @RequestBody SysDepartment sysDepartment) throws Exception {
-        boolean flag = sysDepartmentService.updateSysDepartment(sysDepartment);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 删除部门
-     */
-    @PostMapping("/delete/{id}")
-    @RequiresPermissions("sys:department:delete")
-    @OperationLog(name = "删除部门", type = OperationLogType.DELETE)
-    @ApiOperation(value = "删除部门", response = ApiResult.class)
-    public ApiResult<Boolean> deleteSysDepartment(@PathVariable("id") Long id) throws Exception {
-        boolean flag = sysDepartmentService.deleteSysDepartment(id);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 获取部门
-     */
-    @GetMapping("/info/{id}")
-    @RequiresPermissions("sys:department:info")
-    @OperationLog(name = "部门详情", type = OperationLogType.INFO)
-    @ApiOperation(value = "部门详情", response = SysDepartmentQueryVo.class)
-    public ApiResult<SysDepartmentQueryVo> getSysDepartment(@PathVariable("id") Long id) throws Exception {
-        SysDepartmentQueryVo sysDepartmentQueryVo = sysDepartmentService.getSysDepartmentById(id);
-        return ApiResult.ok(sysDepartmentQueryVo);
-    }
-
-    /**
-     * 部门分页列表
-     */
-    @PostMapping("/getPageList")
-    @RequiresPermissions("sys:department:page")
-    @OperationLog(name = "部门分页列表", type = OperationLogType.PAGE)
-    @ApiOperation(value = "部门分页列表", response = SysDepartmentQueryVo.class)
-    public ApiResult<Paging<SysDepartmentQueryVo>> getSysDepartmentPageList(@Validated @RequestBody SysDepartmentPageParam sysDepartmentPageParam) throws Exception {
-        Paging<SysDepartmentQueryVo> paging = sysDepartmentService.getSysDepartmentPageList(sysDepartmentPageParam);
-        return ApiResult.ok(paging);
-    }
-
-    /**
-     * 获取所有部门列表
-     */
-    @PostMapping("/getAllDepartmentList")
-    @RequiresPermissions("sys:department:all:list")
-    @OperationLog(name = "获取所有部门的树形列表", type = OperationLogType.OTHER_QUERY)
-    @ApiOperation(value = "获取所有部门的树形列表", response = SysDepartment.class)
-    public ApiResult<List<SysDepartment>> getAllDepartmentList() throws Exception {
-        List<SysDepartment> list = sysDepartmentService.getAllDepartmentList();
-        return ApiResult.ok(list);
-    }
-
-    /**
-     * 获取所有部门的树形列表
-     *
-     * @return
-     */
-    @PostMapping("/getDepartmentTree")
-    @RequiresPermissions("sys:department:all:tree")
-    @OperationLog(name = "获取所有部门的树形列表", type = OperationLogType.OTHER_QUERY)
-    @ApiOperation(value = "获取所有部门的树形列表", response = SysDepartmentTreeVo.class)
-    public ApiResult<List<SysDepartmentTreeVo>> getDepartmentTree() throws Exception {
-        List<SysDepartmentTreeVo> treeVos = sysDepartmentService.getDepartmentTree();
-        return ApiResult.ok(treeVos);
-    }
-
-    /**
-     * 部门列表
-     */
-    @PostMapping("/getList")
-    @RequiresPermissions("sys:department:list")
-    @OperationLog(name = "部门列表", type = OperationLogType.LIST)
-    @ApiOperation(value = "部门列表", response = SysDepartment.class)
-    public ApiResult<List<SysDepartment>> getSysDepartmentList() throws Exception {
-        List<SysDepartment> list = sysDepartmentService.list();
-        return ApiResult.ok(list);
-    }
-
-}
-
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/controller/SysPermissionController.java b/system/src/main/java/io/geekidea/springbootplus/system/controller/SysPermissionController.java
deleted file mode 100644
index 46a5e5c0..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/controller/SysPermissionController.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.controller;
-
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.common.controller.BaseController;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-import io.geekidea.springbootplus.system.entity.SysPermission;
-import io.geekidea.springbootplus.system.param.SysPermissionPageParam;
-import io.geekidea.springbootplus.system.service.SysPermissionService;
-import io.geekidea.springbootplus.system.service.SysRolePermissionService;
-import io.geekidea.springbootplus.system.vo.SysPermissionQueryVo;
-import io.geekidea.springbootplus.system.vo.SysPermissionTreeVo;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * <pre>
- * 系统权限 前端控制器
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Slf4j
-@RestController
-@RequestMapping("/sysPermission")
-@Module("system")
-@Api(value = "系统权限 API", tags = {"系统权限"})
-public class SysPermissionController extends BaseController {
-
-    @Autowired
-    private SysPermissionService sysPermissionService;
-
-    @Autowired
-    private SysRolePermissionService sysRolePermissionService;
-
-    /**
-     * 添加系统权限
-     */
-    @PostMapping("/add")
-    @RequiresPermissions("sys:permission:add")
-    @OperationLog(name = "添加系统权限", type = OperationLogType.ADD)
-    @ApiOperation(value = "添加系统权限", response = ApiResult.class)
-    public ApiResult<Boolean> addSysPermission(@Validated @RequestBody SysPermission sysPermission) throws Exception {
-        boolean flag = sysPermissionService.saveSysPermission(sysPermission);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 修改系统权限
-     */
-    @PostMapping("/update")
-    @RequiresPermissions("sys:permission:update")
-    @OperationLog(name = "添加系统权限", type = OperationLogType.UPDATE)
-    @ApiOperation(value = "修改系统权限", response = ApiResult.class)
-    public ApiResult<Boolean> updateSysPermission(@Validated @RequestBody SysPermission sysPermission) throws Exception {
-        boolean flag = sysPermissionService.updateSysPermission(sysPermission);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 删除系统权限
-     */
-    @PostMapping("/delete/{id}")
-    @RequiresPermissions("sys:permission:delete")
-    @OperationLog(name = "删除系统权限", type = OperationLogType.DELETE)
-    @ApiOperation(value = "删除系统权限", response = ApiResult.class)
-    public ApiResult<Boolean> deleteSysPermission(@PathVariable("id") Long id) throws Exception {
-        boolean flag = sysPermissionService.deleteSysPermission(id);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 系统权限详情
-     */
-    @GetMapping("/info/{id}")
-    @RequiresPermissions("sys:permission:info")
-    @OperationLog(name = "系统权限详情", type = OperationLogType.INFO)
-    @ApiOperation(value = "系统权限详情", notes = "", response = SysPermissionQueryVo.class)
-    public ApiResult<SysPermissionQueryVo> getSysPermission(@PathVariable("id") Long id) throws Exception {
-        SysPermissionQueryVo sysPermissionQueryVo = sysPermissionService.getSysPermissionById(id);
-        return ApiResult.ok(sysPermissionQueryVo);
-    }
-
-    /**
-     * 系统权限分页列表
-     */
-    @PostMapping("/getPageList")
-    @RequiresPermissions("sys:permission:page")
-    @OperationLog(name = "系统权限分页列表", type = OperationLogType.PAGE)
-    @ApiOperation(value = "系统权限分页列表", response = SysPermissionQueryVo.class)
-    public ApiResult<Paging<SysPermissionQueryVo>> getSysPermissionPageList(@Validated @RequestBody SysPermissionPageParam sysPermissionPageParam) throws Exception {
-        Paging<SysPermissionQueryVo> paging = sysPermissionService.getSysPermissionPageList(sysPermissionPageParam);
-        return ApiResult.ok(paging);
-    }
-
-    /**
-     * 获取所有菜单列表
-     * @return
-     */
-    @PostMapping("/getAllMenuList")
-    @RequiresPermissions("sys:permission:all:menu:list")
-    @OperationLog(name = "获取所有菜单列表", type = OperationLogType.LIST)
-    @ApiOperation(value = "获取所有菜单列表", response = SysPermission.class)
-    public ApiResult<List<SysPermission>> getAllMenuList() throws Exception {
-        List<SysPermission> list = sysPermissionService.getAllMenuList();
-        return ApiResult.ok(list);
-    }
-
-    /**
-     * 获取获取菜单树形列表
-     * @return
-     */
-    @PostMapping("/getAllMenuTree")
-    @RequiresPermissions("sys:permission:all:menu:tree")
-    @OperationLog(name = "获取获取菜单树形列表", type = OperationLogType.OTHER_QUERY)
-    @ApiOperation(value = "获取获取菜单树形列表", response = SysPermissionTreeVo.class)
-    public ApiResult<List<SysPermissionTreeVo>> getAllMenuTree() throws Exception {
-        List<SysPermissionTreeVo> treeVos = sysPermissionService.getAllMenuTree();
-        return ApiResult.ok(treeVos);
-    }
-
-
-    /**
-     * 根据用户id获取菜单列表
-     * @return
-     */
-    @PostMapping("/getMenuListByUserId/{userId}")
-    @RequiresPermissions("sys:permission:menu:list")
-    @OperationLog(name = "根据用户id获取菜单列表", type = OperationLogType.OTHER_QUERY)
-    @ApiOperation(value = "根据用户id获取菜单列表", response = SysPermission.class)
-    public ApiResult<List<SysPermission>> getMenuListByUserId(@PathVariable("userId") Long userId) throws Exception {
-        List<SysPermission> list = sysPermissionService.getMenuListByUserId(userId);
-        return ApiResult.ok(list);
-    }
-
-    /**
-     * 根据用户id获取菜单树形列表
-     * @return
-     */
-    @PostMapping("/getMenuTreeByUserId/{userId}")
-    @RequiresPermissions("sys:permission:menu:tree")
-    @OperationLog(name = "根据用户id获取菜单树形列表", type = OperationLogType.OTHER_QUERY)
-    @ApiOperation(value = "根据用户id获取菜单树形列表", response = SysPermissionTreeVo.class)
-    public ApiResult<List<SysPermissionTreeVo>> getMenuTreeByUserId(@PathVariable("userId") Long userId) throws Exception {
-        List<SysPermissionTreeVo> treeVos = sysPermissionService.getMenuTreeByUserId(userId);
-        return ApiResult.ok(treeVos);
-    }
-
-    /**
-     * 根据用户id获取该用户所有权限编码
-     * @return
-     */
-    @GetMapping("/getPermissionCodesByUserId/{userId}")
-    @RequiresPermissions("sys:permission:codes")
-    @OperationLog(name = "根据用户id获取该用户所有权限编码", type = OperationLogType.OTHER_QUERY)
-    @ApiOperation(value = "根据用户id获取该用户所有权限编码", response = ApiResult.class)
-    public ApiResult<List<String>> getPermissionCodesByUserId(@PathVariable("userId") Long userId) throws Exception {
-        List<String> list = sysPermissionService.getPermissionCodesByUserId(userId);
-        return ApiResult.ok(list);
-    }
-
-    /**
-     * 根据角色id获取该对应的所有三级权限ID
-     * @return
-     */
-    @GetMapping("/getThreeLevelPermissionIdsByRoleId/{roleId}")
-    @RequiresPermissions("sys:permission:three-ids-by-role-id")
-    @OperationLog(name = "根据角色id获取该对应的所有三级权限ID", type = OperationLogType.OTHER_QUERY)
-    @ApiOperation(value = "根据角色id获取该对应的所有三级权限ID", response = ApiResult.class)
-    public ApiResult<List<Long>> getPermissionIdsByRoleId(@PathVariable("roleId") Long roleId) throws Exception {
-        List<Long> list = sysRolePermissionService.getThreeLevelPermissionIdsByRoleId(roleId);
-        return ApiResult.ok(list);
-    }
-
-    /**
-     * 获取所有导航树形菜单(一级/二级菜单)
-     * @return
-     */
-    @PostMapping("/getNavMenuTree")
-    @RequiresPermissions("sys:permission:nav-menu")
-    @OperationLog(name = "获取所有导航菜单(一级/二级菜单)", type = OperationLogType.OTHER_QUERY)
-    @ApiOperation(value = "获取所有导航菜单(一级/二级菜单)", response = ApiResult.class)
-    public ApiResult<List<SysPermissionTreeVo>> getNavMenuTree() throws Exception {
-        List<SysPermissionTreeVo> list = sysPermissionService.getNavMenuTree();
-        return ApiResult.ok(list);
-    }
-
-}
-
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/controller/SysRoleController.java b/system/src/main/java/io/geekidea/springbootplus/system/controller/SysRoleController.java
deleted file mode 100644
index c5fbd9cc..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/controller/SysRoleController.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.controller;
-
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.common.controller.BaseController;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.core.validator.groups.Add;
-import io.geekidea.springbootplus.framework.core.validator.groups.Update;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-import io.geekidea.springbootplus.system.entity.SysRole;
-import io.geekidea.springbootplus.system.param.sysrole.SysRolePageParam;
-import io.geekidea.springbootplus.system.param.sysrole.UpdateSysRolePermissionParam;
-import io.geekidea.springbootplus.system.service.SysRoleService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-/**
- * <pre>
- * 系统角色 前端控制器
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Slf4j
-@RestController
-@RequestMapping("/sysRole")
-@Module("system")
-@Api(value = "系统角色API", tags = {"系统角色"})
-public class SysRoleController extends BaseController {
-
-    @Autowired
-    private SysRoleService sysRoleService;
-
-    /**
-     * 添加系统角色
-     */
-    @PostMapping("/add")
-    @RequiresPermissions("sys:role:add")
-    @OperationLog(name = "添加系统角色", type = OperationLogType.ADD)
-    @ApiOperation(value = "添加系统角色", response = ApiResult.class)
-    public ApiResult<Boolean> addSysRole(@Validated(Add.class) @RequestBody SysRole sysRole) throws Exception {
-        boolean flag = sysRoleService.saveSysRole(sysRole);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 修改系统角色
-     */
-    @PostMapping("/update")
-    @RequiresPermissions("sys:role:update")
-    @OperationLog(name = "修改系统角色", type = OperationLogType.UPDATE)
-    @ApiOperation(value = "修改系统角色", response = ApiResult.class)
-    public ApiResult<Boolean> updateSysRole(@Validated(Update.class) @RequestBody SysRole sysRole) throws Exception {
-        boolean flag = sysRoleService.updateSysRole(sysRole);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 删除系统角色
-     */
-    @PostMapping("/delete/{id}")
-    @RequiresPermissions("sys:role:delete")
-    @OperationLog(name = "删除系统角色", type = OperationLogType.DELETE)
-    @ApiOperation(value = "删除系统角色", response = ApiResult.class)
-    public ApiResult<Boolean> deleteSysRole(@PathVariable("id") Long id) throws Exception {
-        boolean flag = sysRoleService.deleteSysRole(id);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 获取系统角色
-     */
-    @GetMapping("/info/{id}")
-    @RequiresPermissions("sys:role:info")
-    @OperationLog(name = "系统角色详情", type = OperationLogType.INFO)
-    @ApiOperation(value = "系统角色详情", response = SysRole.class)
-    public ApiResult<SysRole> getSysRole(@PathVariable("id") Long id) throws Exception {
-        SysRole sysRole = sysRoleService.getById(id);
-        return ApiResult.ok(sysRole);
-    }
-
-    /**
-     * 系统角色分页列表
-     */
-    @PostMapping("/getPageList")
-    @RequiresPermissions("sys:role:page")
-    @OperationLog(name = "系统角色分页列表", type = OperationLogType.PAGE)
-    @ApiOperation(value = "系统角色分页列表", response = SysRole.class)
-    public ApiResult<Paging<SysRole>> getSysRolePageList(@Validated @RequestBody SysRolePageParam sysRolePageParam) throws Exception {
-        Paging<SysRole> paging = sysRoleService.getSysRolePageList(sysRolePageParam);
-        return ApiResult.ok(paging);
-    }
-
-    /**
-     * 获取系统角色列表
-     *
-     * @return
-     */
-    @PostMapping("/getList")
-    @RequiresPermissions("sys:role:list")
-    @OperationLog(name = "系统角色列表", type = OperationLogType.LIST)
-    @ApiOperation(value = "系统角色列表", response = SysRole.class)
-    public ApiResult<List<SysRole>> getRoleList() {
-        return ApiResult.ok(sysRoleService.list());
-    }
-
-    /**
-     * 修改系统角色权限
-     */
-    @PostMapping("/updateSysRolePermission")
-    @RequiresPermissions("sys:role-permission:update")
-    @OperationLog(name = "修改系统角色权限", type = OperationLogType.UPDATE)
-    @ApiOperation(value = "修改系统角色权限", response = ApiResult.class)
-    public ApiResult<Boolean> updateSysRolePermission(@Validated @RequestBody UpdateSysRolePermissionParam param) throws Exception {
-        boolean flag = sysRoleService.updateSysRolePermission(param);
-        return ApiResult.result(flag);
-    }
-
-}
-
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/controller/SysUserController.java b/system/src/main/java/io/geekidea/springbootplus/system/controller/SysUserController.java
deleted file mode 100644
index d010fe8a..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/controller/SysUserController.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.controller;
-
-import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties;
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.common.controller.BaseController;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.core.validator.groups.Add;
-import io.geekidea.springbootplus.framework.core.validator.groups.Update;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-import io.geekidea.springbootplus.system.entity.SysUser;
-import io.geekidea.springbootplus.system.param.sysuser.ResetPasswordParam;
-import io.geekidea.springbootplus.system.param.sysuser.SysUserPageParam;
-import io.geekidea.springbootplus.system.param.sysuser.UpdatePasswordParam;
-import io.geekidea.springbootplus.system.param.sysuser.UploadHeadParam;
-import io.geekidea.springbootplus.system.service.SysUserService;
-import io.geekidea.springbootplus.system.vo.SysUserQueryVo;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-/**
- * <pre>
- * 系统用户 前端控制器
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Slf4j
-@RestController
-@RequestMapping("/sysUser")
-@Module("system")
-@Api(value = "系统用户API", tags = {"系统用户"})
-public class SysUserController extends BaseController {
-
-    @Autowired
-    private SysUserService sysUserService;
-
-    @Autowired
-    private SpringBootPlusProperties springBootPlusProperties;
-
-    /**
-     * 添加系统用户
-     */
-    @PostMapping("/add")
-    @RequiresPermissions("sys:user:add")
-    @OperationLog(name = "添加系统用户", type = OperationLogType.ADD)
-    @ApiOperation(value = "添加系统用户", response = ApiResult.class)
-    public ApiResult<Boolean> addSysUser(@Validated(Add.class) @RequestBody SysUser sysUser) throws Exception {
-        boolean flag = sysUserService.saveSysUser(sysUser);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 修改系统用户
-     */
-    @PostMapping("/update")
-    @RequiresPermissions("sys:user:update")
-    @OperationLog(name = "修改系统用户", type = OperationLogType.UPDATE)
-    @ApiOperation(value = "修改系统用户", response = ApiResult.class)
-    public ApiResult<Boolean> updateSysUser(@Validated(Update.class) @RequestBody SysUser sysUser) throws Exception {
-        boolean flag = sysUserService.updateSysUser(sysUser);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 删除系统用户
-     */
-    @PostMapping("/delete/{id}")
-    @RequiresPermissions("sys:user:delete")
-    @OperationLog(name = "删除系统用户", type = OperationLogType.DELETE)
-    @ApiOperation(value = "删除系统用户", response = ApiResult.class)
-    public ApiResult<Boolean> deleteSysUser(@PathVariable("id") Long id) throws Exception {
-        boolean flag = sysUserService.deleteSysUser(id);
-        return ApiResult.result(flag);
-    }
-
-
-    /**
-     * 根据id获取系统用户
-     */
-    @GetMapping("/info/{id}")
-    @RequiresPermissions("sys:user:info:id")
-    @OperationLog(name = "系统用户详情", type = OperationLogType.INFO)
-    @ApiOperation(value = "系统用户详情", notes = "", response = SysUserQueryVo.class)
-    public ApiResult<SysUserQueryVo> getSysUser(@PathVariable("id") Long id) throws Exception {
-        SysUserQueryVo sysUserQueryVo = sysUserService.getSysUserById(id);
-        return ApiResult.ok(sysUserQueryVo);
-    }
-
-    /**
-     * 系统用户分页列表
-     */
-    @PostMapping("/getPageList")
-    @RequiresPermissions("sys:user:page")
-    @OperationLog(name = "系统用户分页列表", type = OperationLogType.PAGE)
-    @ApiOperation(value = "系统用户分页列表", response = SysUserQueryVo.class)
-    public ApiResult<Paging<SysUserQueryVo>> getSysUserPageList(@Validated @RequestBody SysUserPageParam sysUserPageParam) throws Exception {
-        Paging<SysUserQueryVo> paging = sysUserService.getSysUserPageList(sysUserPageParam);
-        return ApiResult.ok(paging);
-    }
-
-    /**
-     * 修改密码
-     */
-    @PostMapping("/updatePassword")
-    @RequiresPermissions("sys:user:update:password")
-    @OperationLog(name = "修改密码", type = OperationLogType.UPDATE)
-    @ApiOperation(value = "修改密码", response = ApiResult.class)
-    public ApiResult<Boolean> updatePassword(@Validated @RequestBody UpdatePasswordParam updatePasswordParam) throws Exception {
-        boolean flag = sysUserService.updatePassword(updatePasswordParam);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 管理员重置用户密码
-     */
-    @PostMapping("/resetPassword")
-    @RequiresPermissions("sys:user:reset:password")
-    @OperationLog(name = "管理员重置用户密码", type = OperationLogType.UPDATE)
-    @ApiOperation(value = "管理员重置用户密码", response = ApiResult.class)
-    public ApiResult<Boolean> resetPassword(@Validated @RequestBody ResetPasswordParam resetPasswordParam) throws Exception {
-        boolean flag = sysUserService.resetPassword(resetPasswordParam);
-        return ApiResult.result(flag);
-    }
-
-    /**
-     * 修改头像
-     */
-    @PostMapping("/uploadHead")
-    @RequiresPermissions("sys:user:update:head")
-    @OperationLog(name = "修改头像", type = OperationLogType.UPDATE)
-    @ApiOperation(value = "修改头像", response = ApiResult.class)
-    public ApiResult<Boolean> uploadHead(@Validated @RequestBody UploadHeadParam uploadHeadParam) throws Exception {
-        boolean flag = sysUserService.updateSysUserHead(uploadHeadParam.getId(), uploadHeadParam.getHead());
-        return ApiResult.result(flag);
-    }
-}
-
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/controller/UploadController.java b/system/src/main/java/io/geekidea/springbootplus/system/controller/UploadController.java
deleted file mode 100644
index 7f22b411..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/controller/UploadController.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.controller;
-
-import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties;
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-import io.geekidea.springbootplus.framework.util.UploadUtil;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiImplicitParam;
-import io.swagger.annotations.ApiImplicitParams;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.lang3.RandomStringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-
-/**
- * 上传控制器
- *
- * @author geekidea
- * @date 2019/8/20
- * @since 1.2.1-RELEASE
- */
-@Slf4j
-@RestController
-@RequestMapping("/upload")
-@Module("system")
-@Api(value = "文件上传", tags = {"文件上传"})
-public class UploadController {
-
-    @Autowired
-    private SpringBootPlusProperties springBootPlusProperties;
-
-    /**
-     * 上传单个文件
-     * @return
-     */
-    @PostMapping
-    @OperationLog(name = "上传单个文件", type = OperationLogType.UPLOAD)
-    @ApiOperation(value = "上传单个文件", response = ApiResult.class)
-    @ApiImplicitParams({
-            @ApiImplicitParam(name = "file", value = "文件", required = true,dataType = "__file"),
-            @ApiImplicitParam(name = "type", value = "类型 head:头像",required = true)
-    })
-    public ApiResult<String> upload(@RequestParam("file") MultipartFile multipartFile,
-                                    @RequestParam("type") String type) throws Exception {
-        log.info("multipartFile = " + multipartFile);
-        log.info("ContentType = " + multipartFile.getContentType());
-        log.info("OriginalFilename = " + multipartFile.getOriginalFilename());
-        log.info("Name = " + multipartFile.getName());
-        log.info("Size = " + multipartFile.getSize());
-        log.info("type = " + type);
-
-        // 上传文件，返回保存的文件名称
-        String saveFileName = UploadUtil.upload(springBootPlusProperties.getUploadPath(), multipartFile, originalFilename -> {
-            // 文件后缀
-            String fileExtension = FilenameUtils.getExtension(originalFilename);
-            // 这里可自定义文件名称，比如按照业务类型/文件格式/日期
-            String dateString = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssS")) + RandomStringUtils.randomNumeric(6);
-            String fileName = dateString + "." + fileExtension;
-            return fileName;
-        });
-
-        // 上传成功之后，返回访问路径，请根据实际情况设置
-
-        String fileAccessPath = springBootPlusProperties.getResourceAccessUrl() + saveFileName;
-        log.info("fileAccessPath:{}", fileAccessPath);
-
-        return ApiResult.ok(fileAccessPath);
-    }
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/controller/VerificationCodeController.java b/system/src/main/java/io/geekidea/springbootplus/system/controller/VerificationCodeController.java
deleted file mode 100644
index d8366674..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/controller/VerificationCodeController.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.controller;
-
-import io.geekidea.springbootplus.config.constant.CommonConstant;
-import io.geekidea.springbootplus.config.constant.CommonRedisKey;
-import io.geekidea.springbootplus.framework.common.api.ApiResult;
-import io.geekidea.springbootplus.framework.log.annotation.Module;
-import io.geekidea.springbootplus.framework.log.annotation.OperationLog;
-import io.geekidea.springbootplus.framework.log.enums.OperationLogType;
-import io.geekidea.springbootplus.framework.util.UUIDUtil;
-import io.geekidea.springbootplus.framework.util.VerificationCode;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.http.MediaType;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-import javax.imageio.ImageIO;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.HttpServletResponse;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayOutputStream;
-import java.util.Base64;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-/**
- * 验证码接口
- *
- * @author geekidea
- * @date 2019-10-27
- **/
-@Slf4j
-@Controller
-@Api(value = "验证码API", tags = {"验证码"})
-@Module("system")
-@RequestMapping("/verificationCode")
-@ConditionalOnProperty(value = {"spring-boot-plus.enable-verify-code"}, matchIfMissing = true)
-public class VerificationCodeController {
-
-    @Autowired
-    private RedisTemplate<String, String> redisTemplate;
-
-    /**
-     * 获取验证码
-     */
-    @GetMapping("/getImage")
-    @OperationLog(name = "获取验证码", type = OperationLogType.OTHER)
-    @ApiOperation(value = "获取验证码", response = ApiResult.class)
-    public void getImage(HttpServletResponse response) throws Exception {
-        VerificationCode verificationCode = new VerificationCode();
-        BufferedImage image = verificationCode.getImage();
-        String code = verificationCode.getText();
-        String verifyToken = UUIDUtil.getUuid();
-        // 缓存到Redis
-        redisTemplate.opsForValue().set(String.format(CommonRedisKey.VERIFY_CODE, verifyToken), code, 5, TimeUnit.MINUTES);
-        response.setHeader(CommonConstant.VERIFY_TOKEN, verifyToken);
-        response.setContentType(MediaType.IMAGE_JPEG_VALUE);
-        response.setHeader("Pragma", "No-cache");
-        response.setHeader("Cache-Control", "no-cache");
-        response.setDateHeader("Expire", 0);
-        ServletOutputStream outputStream = response.getOutputStream();
-        ImageIO.write(image, CommonConstant.JPEG, outputStream);
-    }
-
-    /**
-     * 获取图片Base64验证码
-     */
-    @GetMapping("/getBase64Image")
-    @ResponseBody
-    @OperationLog(name = "获取图片Base64验证码", type = OperationLogType.OTHER)
-    @ApiOperation(value = "获取图片Base64验证码", response = ApiResult.class)
-    public ApiResult<Map<String, Object>> getCode(HttpServletResponse response) throws Exception {
-        VerificationCode verificationCode = new VerificationCode();
-        BufferedImage image = verificationCode.getImage();
-        String code = verificationCode.getText();
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        ImageIO.write(image, CommonConstant.JPEG, outputStream);
-        // 将图片转换成base64字符串
-        String base64 = Base64.getEncoder().encodeToString(outputStream.toByteArray());
-        // 生成当前验证码会话token
-        String verifyToken = UUIDUtil.getUuid();
-        Map<String, Object> map = new HashMap<>(2);
-        map.put(CommonConstant.IMAGE, CommonConstant.BASE64_PREFIX + base64);
-        map.put(CommonConstant.VERIFY_TOKEN, verifyToken);
-        // 缓存到Redis
-        redisTemplate.opsForValue().set(String.format(CommonRedisKey.VERIFY_CODE, verifyToken), code, 5, TimeUnit.MINUTES);
-        return ApiResult.ok(map);
-    }
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/convert/SysDepartmentConvert.java b/system/src/main/java/io/geekidea/springbootplus/system/convert/SysDepartmentConvert.java
deleted file mode 100644
index dbb038d3..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/convert/SysDepartmentConvert.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.convert;
-
-import io.geekidea.springbootplus.system.entity.SysDepartment;
-import io.geekidea.springbootplus.system.vo.SysDepartmentTreeVo;
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-
-import java.util.List;
-
-/**
- * 部门对象属性转换器
- *
- * @author geekidea
- * @date 2019-11-01
- **/
-@Mapper
-public interface SysDepartmentConvert {
-
-    SysDepartmentConvert INSTANCE = Mappers.getMapper(SysDepartmentConvert.class);
-
-    /**
-     * SysDepartment转换成SysDepartmentTreeVo对象
-     *
-     * @param sysDepartment
-     * @return
-     */
-    SysDepartmentTreeVo entityToTreeVo(SysDepartment sysDepartment);
-
-    /**
-     * SysDepartment列表转换成SysDepartmentTreeVo列表
-     *
-     * @param list
-     * @return
-     */
-    List<SysDepartmentTreeVo> listToTreeVoList(List<SysDepartment> list);
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/convert/SysPermissionConvert.java b/system/src/main/java/io/geekidea/springbootplus/system/convert/SysPermissionConvert.java
deleted file mode 100644
index 803bf788..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/convert/SysPermissionConvert.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.convert;
-
-import io.geekidea.springbootplus.system.entity.SysPermission;
-import io.geekidea.springbootplus.system.vo.SysPermissionTreeVo;
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-
-/**
- * SysPermission类对象书香转换器
-/ *
- * @author geekidea
- * @date 2019-10-26
- **/
-@Mapper
-public interface SysPermissionConvert {
-
-    SysPermissionConvert INSTANCE = Mappers.getMapper(SysPermissionConvert.class);
-
-    /**
-     * SysPermission对象转换成SysPermissionTreeVo对象
-     *
-     * @param sysPermission
-     * @return
-     */
-    SysPermissionTreeVo permissionToTreeVo(SysPermission sysPermission);
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/convert/SysRoleConvert.java b/system/src/main/java/io/geekidea/springbootplus/system/convert/SysRoleConvert.java
deleted file mode 100644
index 569eeea4..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/convert/SysRoleConvert.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.convert;
-
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-
-/**
- * 系统角色对象属性转换器
- *
- * @author geekidea
- * @date 2019-10-05
- **/
-@Mapper
-public interface SysRoleConvert {
-
-    SysRoleConvert INSTANCE = Mappers.getMapper(SysRoleConvert.class);
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/convert/SysUserConvert.java b/system/src/main/java/io/geekidea/springbootplus/system/convert/SysUserConvert.java
deleted file mode 100644
index bd858629..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/convert/SysUserConvert.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.convert;
-
-import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
-import org.mapstruct.Mapper;
-import org.mapstruct.factory.Mappers;
-
-/**
- * 系统用户对象属性转换器
- *
- * @author geekidea
- * @date 2019-10-05
- **/
-@Mapper
-public interface SysUserConvert {
-
-    SysUserConvert INSTANCE = Mappers.getMapper(SysUserConvert.class);
-
-    /**
-     * 系统用户实体对象转换成登录用户VO对象
-     *
-     * @param sysUser
-     * @return
-     */
-    LoginSysUserVo sysUserToLoginSysUserVo(io.geekidea.springbootplus.system.entity.SysUser sysUser);
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/entity/SysDepartment.java b/system/src/main/java/io/geekidea/springbootplus/system/entity/SysDepartment.java
deleted file mode 100644
index d3aeae61..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/entity/SysDepartment.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.Version;
-import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.Null;
-import java.util.Date;
-
-/**
- * <pre>
- * 部门
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "SysDepartment对象", description = "部门")
-public class SysDepartment extends BaseEntity {
-
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty("主键")
-    @TableId(value = "id", type = IdType.AUTO)
-    private Long id;
-
-    @ApiModelProperty("部门名称")
-    @NotBlank(message = "部门名称不能为空")
-    private String name;
-
-    @ApiModelProperty("父id")
-    private Long parentId;
-
-    @ApiModelProperty("状态，0：禁用，1：启用")
-    private Integer state;
-
-    @ApiModelProperty("排序")
-    private Integer sort;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("版本")
-    @Null(message = "版本不用传")
-    @Version
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    @Null(message = "创建时间不用传")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    @Null(message = "修改时间不用传")
-    private Date updateTime;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/entity/SysPermission.java b/system/src/main/java/io/geekidea/springbootplus/system/entity/SysPermission.java
deleted file mode 100644
index 40ebeac5..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/entity/SysPermission.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.Version;
-import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Null;
-import java.util.Date;
-
-/**
- * <pre>
- * 系统权限
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "SysPermission对象", description = "系统权限")
-public class SysPermission extends BaseEntity {
-
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty("主键")
-    @TableId(value = "id", type = IdType.AUTO)
-    private Long id;
-
-    @ApiModelProperty("权限名称")
-    private String name;
-
-    @ApiModelProperty("父id")
-    private Long parentId;
-
-    @ApiModelProperty("路径")
-    private String url;
-
-    @ApiModelProperty("唯一编码")
-    @NotBlank(message = "唯一编码不能为空")
-    private String code;
-
-    @ApiModelProperty("图标")
-    private String icon;
-
-    @ApiModelProperty("类型，1：菜单，2：按钮")
-    @NotNull(message = "类型，1：菜单，2：按钮不能为空")
-    private Integer type;
-
-    @ApiModelProperty("层级，1：第一级，2：第二级，N：第N级")
-    @NotNull(message = "层级，1：第一级，2：第二级，N：第N级不能为空")
-    private Integer level;
-
-    @ApiModelProperty("状态，0：禁用，1：启用")
-    private Integer state;
-
-    @ApiModelProperty("排序")
-    private Integer sort;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("版本")
-    @Null(message = "版本不用传")
-    @Version
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    @Null(message = "创建时间不用传")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    @Null(message = "修改时间不用传")
-    private Date updateTime;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/entity/SysRole.java b/system/src/main/java/io/geekidea/springbootplus/system/entity/SysRole.java
deleted file mode 100644
index 3770b591..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/entity/SysRole.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.Version;
-import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
-import io.geekidea.springbootplus.framework.core.validator.groups.Update;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Null;
-import java.util.Date;
-
-/**
- * <pre>
- * 系统角色
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "SysRole对象", description = "系统角色")
-public class SysRole extends BaseEntity {
-
-    private static final long serialVersionUID = -487738234353456553L;
-
-    @ApiModelProperty("主键")
-    @TableId(value = "id", type = IdType.AUTO)
-    @NotNull(groups = Update.class, message = "角色ID不能为空")
-    private Long id;
-
-    @ApiModelProperty("角色名称")
-    @NotBlank(message = "角色名称不能为空")
-    private String name;
-
-    @ApiModelProperty("角色唯一编码")
-    private String code;
-
-    @ApiModelProperty("角色类型")
-    private Integer type;
-
-    @ApiModelProperty("角色状态，0：禁用，1：启用")
-    private Integer state;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("版本")
-    @Null(message = "版本不用传")
-    @Version
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    @Null(message = "创建时间不用传")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    @Null(message = "修改时间不用传")
-    private Date updateTime;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/entity/SysRolePermission.java b/system/src/main/java/io/geekidea/springbootplus/system/entity/SysRolePermission.java
deleted file mode 100644
index 7e4942c4..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/entity/SysRolePermission.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.Version;
-import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Null;
-import java.util.Date;
-
-/**
- * <pre>
- * 角色权限关系
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "SysRolePermission对象", description = "角色权限关系")
-public class SysRolePermission extends BaseEntity {
-
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty("主键")
-    @TableId(value = "id", type = IdType.AUTO)
-    private Long id;
-
-    @ApiModelProperty("角色id")
-    @NotNull(message = "角色id不能为空")
-    private Long roleId;
-
-    @ApiModelProperty("权限id")
-    @NotNull(message = "权限id不能为空")
-    private Long permissionId;
-
-    @ApiModelProperty("状态，0：禁用，1：启用")
-    private Integer state;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("版本")
-    @Null(message = "版本不用传")
-    @Version
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    @Null(message = "创建时间不用传")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    @Null(message = "修改时间不用传")
-    private Date updateTime;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/entity/SysUser.java b/system/src/main/java/io/geekidea/springbootplus/system/entity/SysUser.java
deleted file mode 100644
index 8f150a7e..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/entity/SysUser.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.entity;
-
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableLogic;
-import com.baomidou.mybatisplus.annotation.Version;
-import io.geekidea.springbootplus.framework.common.entity.BaseEntity;
-import io.geekidea.springbootplus.framework.core.validator.groups.Add;
-import io.geekidea.springbootplus.framework.core.validator.groups.Update;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Null;
-import java.util.Date;
-
-/**
- * <pre>
- * 系统用户
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "SysUser对象", description = "系统用户")
-public class SysUser extends BaseEntity {
-
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty("主键")
-    @NotNull(message = "ID不能为空", groups = {Update.class})
-    @TableId(value = "id", type = IdType.AUTO)
-    private Long id;
-
-    @ApiModelProperty("用户名")
-    @NotNull(message = "用户名不能为空", groups = {Add.class})
-    private String username;
-
-    @ApiModelProperty("昵称")
-    private String nickname;
-
-    @ApiModelProperty("密码")
-    private String password;
-
-    @ApiModelProperty("盐值")
-    private String salt;
-
-    @ApiModelProperty("手机号码")
-    @NotBlank(message = "手机号码不能为空")
-    private String phone;
-
-    @ApiModelProperty("性别，0：女，1：男，默认1")
-    private Integer gender;
-
-    @ApiModelProperty("头像")
-    private String head;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("状态，0：禁用，1：启用，2：锁定")
-    private Integer state;
-
-    @ApiModelProperty("部门id")
-    @NotNull(message = "部门id不能为空")
-    private Long departmentId;
-
-    @ApiModelProperty("角色id")
-    @NotNull(message = "角色id不能为空")
-    private Long roleId;
-
-    @ApiModelProperty("逻辑删除，0：未删除，1：已删除")
-    @Null(message = "逻辑删除不用传")
-    @TableLogic
-    private Integer deleted;
-
-    @ApiModelProperty("版本")
-    @Null(message = "版本不用传")
-    @Version
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    @Null(message = "创建时间不用传")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    @Null(message = "修改时间不用传")
-    private Date updateTime;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/enums/MenuLevelEnum.java b/system/src/main/java/io/geekidea/springbootplus/system/enums/MenuLevelEnum.java
deleted file mode 100644
index 30de05f5..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/enums/MenuLevelEnum.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.enums;
-
-import io.geekidea.springbootplus.framework.common.enums.BaseEnum;
-
-/**
- * 层级枚举
- * @author geekidea
- * @date 2019-10-24
- **/
-public enum MenuLevelEnum implements BaseEnum {
-    /** 一级菜单 **/
-    ONE(1, "一级菜单"),
-    /** 二级菜单 **/
-    TWO(2, "二级菜单"),
-    /** 功能菜单 **/
-    THREE(3, "功能菜单");
-
-    private Integer code;
-    private String desc;
-
-    MenuLevelEnum(Integer code, String desc) {
-        this.code = code;
-        this.desc = desc;
-    }
-
-    @Override
-    public Integer getCode() {
-        return this.code;
-    }
-
-    @Override
-    public String getDesc() {
-        return this.desc;
-    }
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/enums/StateEnum.java b/system/src/main/java/io/geekidea/springbootplus/system/enums/StateEnum.java
deleted file mode 100644
index 54dfd17e..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/enums/StateEnum.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.enums;
-
-import io.geekidea.springbootplus.framework.common.enums.BaseEnum;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * 启用禁用状态枚举
- *
- * @author geekidea
- * @date 2019-10-24
- **/
-@Getter
-@AllArgsConstructor
-public enum StateEnum implements BaseEnum {
-
-    /** 禁用 **/
-    DISABLE(0, "禁用"),
-    /** 启用 **/
-    ENABLE(1, "启用");
-
-    private Integer code;
-    private String desc;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/exception/SysLoginException.java b/system/src/main/java/io/geekidea/springbootplus/system/exception/SysLoginException.java
deleted file mode 100644
index ee83e8d6..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/exception/SysLoginException.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.exception;
-
-import io.geekidea.springbootplus.framework.common.api.ApiCode;
-import io.geekidea.springbootplus.framework.common.exception.SpringBootPlusException;
-
-/**
- * 系统登录异常
- *
- * @author geekidea
- * @date 2019-08-04
- */
-public class SysLoginException extends SpringBootPlusException {
-	private static final long serialVersionUID = -3157438982569715170L;
-
-	public SysLoginException(String message) {
-        super(message);
-    }
-
-    public SysLoginException(Integer errorCode, String message) {
-        super(errorCode, message);
-    }
-
-    public SysLoginException(ApiCode apiCode) {
-        super(apiCode);
-    }
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/exception/VerificationCodeException.java b/system/src/main/java/io/geekidea/springbootplus/system/exception/VerificationCodeException.java
deleted file mode 100644
index 5ea91ae3..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/exception/VerificationCodeException.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.exception;
-
-import io.geekidea.springbootplus.framework.common.api.ApiCode;
-import io.geekidea.springbootplus.framework.common.exception.SpringBootPlusException;
-
-/**
- * 验证码校验异常
- *
- * @author geekidea
- * @date 2018-11-08
- */
-public class VerificationCodeException extends SpringBootPlusException {
-	private static final long serialVersionUID = -2640690119865434398L;
-
-	public VerificationCodeException(String message) {
-        super(message);
-    }
-
-    public VerificationCodeException(Integer errorCode, String message) {
-        super(errorCode, message);
-    }
-
-    public VerificationCodeException(ApiCode apiCode) {
-        super(apiCode);
-    }
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/interceptor/DownloadInterceptor.java b/system/src/main/java/io/geekidea/springbootplus/system/interceptor/DownloadInterceptor.java
deleted file mode 100644
index 409ecae8..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/interceptor/DownloadInterceptor.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.interceptor;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * 下载拦截器
- *
- * @author geekidea
- * @date 2019/8/21
- */
-@Slf4j
-@ConditionalOnProperty(value = {"spring-boot-plus.interceptor.download.enable"}, matchIfMissing = true)
-public class DownloadInterceptor extends HandlerInterceptorAdapter {
-
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        // 如果访问的不是控制器,则跳出,继续执行下一个拦截器
-        if (!(handler instanceof HandlerMethod)) {
-            return true;
-        }
-        // 访问路径
-        String url = request.getRequestURI();
-        // 访问全路径
-        String fullUrl = request.getRequestURL().toString();
-        // 下载拦截器，业务处理代码
-        log.debug("DownloadInterceptor...");
-        // 访问token，如果需要，可以设置参数，进行鉴权
-//        String token = request.getParameter(JwtTokenUtil.getTokenName());
-        return true;
-    }
-
-    @Override
-    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
-        // 记录实际下载日志...
-    }
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/interceptor/ResourceInterceptor.java b/system/src/main/java/io/geekidea/springbootplus/system/interceptor/ResourceInterceptor.java
deleted file mode 100644
index d02e5a38..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/interceptor/ResourceInterceptor.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.interceptor;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * 资源拦截器
- *
- * @author geekidea
- * @date 2019/8/20
- */
-@Slf4j
-@ConditionalOnProperty(value = {"spring-boot-plus.interceptor.resource.enable"}, matchIfMissing = true)
-public class ResourceInterceptor extends HandlerInterceptorAdapter {
-
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        // 如果访问的控制器,则跳出,继续执行下一个拦截器
-        if (handler instanceof HandlerMethod) {
-            return true;
-        }
-        // 访问路径
-        String url = request.getRequestURI();
-        // 访问全路径
-        String fullUrl = request.getRequestURL().toString();
-        // 资源拦截器，业务处理代码
-        log.debug("ResourceInterceptor...");
-        // 访问token，如果需要，可以设置参数，进行鉴权
-//         String token = request.getParameter(JwtTokenUtil.getTokenName());
-        return true;
-    }
-
-    @Override
-    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
-        // 记录实际访问图片日志...
-    }
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/interceptor/UploadInterceptor.java b/system/src/main/java/io/geekidea/springbootplus/system/interceptor/UploadInterceptor.java
deleted file mode 100644
index f5e83432..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/interceptor/UploadInterceptor.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.interceptor;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * 上传拦截器
- *
- * @author geekidea
- * @date 2019/8/22
- */
-@Slf4j
-@ConditionalOnProperty(value = {"spring-boot-plus.interceptor.upload.enable"}, matchIfMissing = true)
-public class UploadInterceptor extends HandlerInterceptorAdapter {
-
-    @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        // 如果访问的不是控制器,则跳出,继续执行下一个拦截器
-        if (!(handler instanceof HandlerMethod)) {
-            return true;
-        }
-
-        // 访问路径
-        String url = request.getRequestURI();
-        // 访问全路径
-        String fullUrl = request.getRequestURL().toString();
-        // 上传拦截器，业务处理代码
-        log.debug("UploadInterceptor...");
-        // 访问token，如果需要，可以设置参数，进行鉴权
-//        String token = request.getParameter(JwtTokenUtil.getTokenName());
-        return true;
-    }
-
-    @Override
-    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
-        // 记录实际上传日志...
-    }
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysDepartmentMapper.java b/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysDepartmentMapper.java
deleted file mode 100644
index abc7cf9b..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysDepartmentMapper.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import io.geekidea.springbootplus.system.entity.SysDepartment;
-import io.geekidea.springbootplus.system.param.SysDepartmentPageParam;
-import io.geekidea.springbootplus.system.vo.SysDepartmentQueryVo;
-import org.apache.ibatis.annotations.Param;
-import org.springframework.stereotype.Repository;
-
-import java.io.Serializable;
-
-/**
- * <pre>
- * 部门 Mapper 接口
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Repository
-public interface SysDepartmentMapper extends BaseMapper<SysDepartment> {
-
-    /**
-     * 根据ID获取查询对象
-     *
-     * @param id
-     * @return
-     */
-    SysDepartmentQueryVo getSysDepartmentById(Serializable id);
-
-    /**
-     * 获取分页对象
-     *
-     * @param page
-     * @param sysDepartmentPageParam
-     * @return
-     */
-    IPage<SysDepartmentQueryVo> getSysDepartmentPageList(@Param("page") Page page, @Param("param") SysDepartmentPageParam sysDepartmentPageParam);
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysPermissionMapper.java b/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysPermissionMapper.java
deleted file mode 100644
index 6e3ff7e7..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysPermissionMapper.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import io.geekidea.springbootplus.system.entity.SysPermission;
-import io.geekidea.springbootplus.system.param.SysPermissionPageParam;
-import io.geekidea.springbootplus.system.vo.SysPermissionQueryVo;
-import org.apache.ibatis.annotations.Param;
-import org.springframework.stereotype.Repository;
-
-import java.io.Serializable;
-import java.util.List;
-
-/**
- * <pre>
- * 系统权限 Mapper 接口
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Repository
-public interface SysPermissionMapper extends BaseMapper<SysPermission> {
-
-    /**
-     * 根据ID获取查询对象
-     *
-     * @param id
-     * @return
-     */
-    SysPermissionQueryVo getSysPermissionById(Serializable id);
-
-    /**
-     * 获取分页对象
-     *
-     * @param page
-     * @param sysPermissionPageParam
-     * @return
-     */
-    IPage<SysPermissionQueryVo> getSysPermissionPageList(@Param("page") Page page, @Param("param") SysPermissionPageParam sysPermissionPageParam);
-
-    /**
-     * 根据用户id获取该用户所有权限编码
-     *
-     * @param userId
-     * @return
-     * @throws Exception
-     */
-    List<String> getPermissionCodesByUserId(@Param("userId") Long userId);
-
-    /**
-     * 根据用户id获取菜单列表
-     *
-     * @param userId
-     * @return
-     */
-    List<SysPermission> getMenuListByUserId(@Param("userId") Long userId);
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysRoleMapper.java b/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysRoleMapper.java
deleted file mode 100644
index 95348b36..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysRoleMapper.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import io.geekidea.springbootplus.system.entity.SysRole;
-import io.geekidea.springbootplus.system.param.sysrole.SysRolePageParam;
-import io.geekidea.springbootplus.system.vo.SysRoleQueryVo;
-import org.apache.ibatis.annotations.Param;
-import org.springframework.stereotype.Repository;
-
-import java.io.Serializable;
-
-/**
- * <pre>
- * 系统角色 Mapper 接口
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Repository
-public interface SysRoleMapper extends BaseMapper<SysRole> {
-
-    /**
-     * 根据ID获取查询对象
-     *
-     * @param id
-     * @return
-     */
-    SysRoleQueryVo getSysRoleById(Serializable id);
-
-    /**
-     * 获取分页对象
-     *
-     * @param page
-     * @param sysRolePageParam
-     * @return
-     */
-    IPage<SysRoleQueryVo> getSysRolePageList(@Param("page") Page page, @Param("param") SysRolePageParam sysRolePageParam);
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysRolePermissionMapper.java b/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysRolePermissionMapper.java
deleted file mode 100644
index 6fb323f2..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysRolePermissionMapper.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import io.geekidea.springbootplus.system.entity.SysRolePermission;
-import io.geekidea.springbootplus.system.param.sysrole.SysRolePermissionPageParam;
-import io.geekidea.springbootplus.system.vo.SysRolePermissionQueryVo;
-import org.apache.ibatis.annotations.Param;
-import org.springframework.stereotype.Repository;
-
-import java.io.Serializable;
-import java.util.List;
-import java.util.Set;
-
-/**
- * <pre>
- * 角色权限关系 Mapper 接口
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Repository
-public interface SysRolePermissionMapper extends BaseMapper<SysRolePermission> {
-
-    /**
-     * 根据ID获取查询对象
-     *
-     * @param id
-     * @return
-     */
-    SysRolePermissionQueryVo getSysRolePermissionById(Serializable id);
-
-    /**
-     * 获取分页对象
-     *
-     * @param page
-     * @param sysRolePermissionPageParam
-     * @return
-     */
-    IPage<SysRolePermissionQueryVo> getSysRolePermissionPageList(@Param("page") Page page, @Param("param") SysRolePermissionPageParam sysRolePermissionPageParam);
-
-    /**
-     * 根据角色id获取可用的权限编码
-     *
-     * @param roleId
-     * @return
-     */
-    Set<String> getPermissionCodesByRoleId(@Param("roleId") Long roleId);
-
-    /**
-     * 根据角色id获取该对应的所有三级权限ID
-     * @param roleId
-     * @return
-     */
-    List<Long> getThreeLevelPermissionIdsByRoleId(@Param("roleId") Long roleId);
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysUserMapper.java b/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysUserMapper.java
deleted file mode 100644
index 64073d86..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/mapper/SysUserMapper.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.mapper;
-
-import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import io.geekidea.springbootplus.system.entity.SysUser;
-import io.geekidea.springbootplus.system.param.sysuser.SysUserPageParam;
-import io.geekidea.springbootplus.system.vo.SysUserQueryVo;
-import org.apache.ibatis.annotations.Param;
-import org.springframework.stereotype.Repository;
-
-import java.io.Serializable;
-
-/**
- * <pre>
- * 系统用户 Mapper 接口
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Repository
-public interface SysUserMapper extends BaseMapper<SysUser> {
-
-    /**
-     * 根据ID获取查询对象
-     *
-     * @param id
-     * @return
-     */
-    SysUserQueryVo getSysUserById(Serializable id);
-
-    /**
-     * 获取分页对象
-     *
-     * @param page
-     * @param sysUserPageParam
-     * @return
-     */
-    IPage<SysUserQueryVo> getSysUserPageList(@Param("page") Page page, @Param("param") SysUserPageParam sysUserPageParam);
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/param/LoginParam.java b/system/src/main/java/io/geekidea/springbootplus/system/param/LoginParam.java
deleted file mode 100644
index 259c932e..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/param/LoginParam.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.param;
-
-import io.geekidea.springbootplus.framework.shiro.service.LoginUsername;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import javax.validation.constraints.NotBlank;
-
-/**
- * 登录参数
- *
- * @author geekidea
- * @date 2019-05-15
- **/
-@Data
-@ApiModel("登录参数")
-public class LoginParam implements LoginUsername {
-	private static final long serialVersionUID = 2854217576695117356L;
-
-	@NotBlank(message = "请输入账号")
-    @ApiModelProperty(value = "账号", example = "admin")
-    private String username;
-
-    @NotBlank(message = "请输入密码")
-    @ApiModelProperty(value = "密码", example = "123456")
-    private String password;
-
-    @ApiModelProperty("验证码Token")
-    private String verifyToken;
-
-    @ApiModelProperty("验证码")
-    private String code;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/param/SysDepartmentPageParam.java b/system/src/main/java/io/geekidea/springbootplus/system/param/SysDepartmentPageParam.java
deleted file mode 100644
index 24acea2d..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/param/SysDepartmentPageParam.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.param;
-
-import io.geekidea.springbootplus.framework.core.pagination.BasePageOrderParam;
-import io.swagger.annotations.ApiModel;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-/**
- * <pre>
- * 部门 查询参数对象
- * </pre>
- *
- * @author geekidea
- * @date 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "SysDepartmentPageParam对象", description = "部门查询参数")
-public class SysDepartmentPageParam extends BasePageOrderParam {
-    private static final long serialVersionUID = 1L;
-
-
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/param/SysPermissionPageParam.java b/system/src/main/java/io/geekidea/springbootplus/system/param/SysPermissionPageParam.java
deleted file mode 100644
index 98c79bbd..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/param/SysPermissionPageParam.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.param;
-
-import io.geekidea.springbootplus.framework.core.pagination.BasePageOrderParam;
-import io.swagger.annotations.ApiModel;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-/**
- * <pre>
- * 系统权限 查询参数对象
- * </pre>
- *
- * @author geekidea
- * @date 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "SysPermissionPageParam对象", description = "系统权限查询参数")
-public class SysPermissionPageParam extends BasePageOrderParam {
-    private static final long serialVersionUID = 1L;
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/param/sysrole/SysRolePageParam.java b/system/src/main/java/io/geekidea/springbootplus/system/param/sysrole/SysRolePageParam.java
deleted file mode 100644
index b46a0feb..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/param/sysrole/SysRolePageParam.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.param.sysrole;
-
-import io.geekidea.springbootplus.framework.core.pagination.BasePageOrderParam;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-/**
- * <pre>
- * 系统角色 查询参数对象
- * </pre>
- *
- * @author geekidea
- * @date 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "SysRolePageParam对象", description = "系统角色查询参数")
-public class SysRolePageParam extends BasePageOrderParam {
-
-    private static final long serialVersionUID = 5068991832024325736L;
-
-    @ApiModelProperty("角色名称")
-    private String name;
-
-    @ApiModelProperty("角色编码")
-    private String code;
-
-    @ApiModelProperty("角色状态，0：禁用，1：启用")
-    private Integer state;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/param/sysrole/SysRolePermissionPageParam.java b/system/src/main/java/io/geekidea/springbootplus/system/param/sysrole/SysRolePermissionPageParam.java
deleted file mode 100644
index 0782359d..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/param/sysrole/SysRolePermissionPageParam.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.param.sysrole;
-
-import io.geekidea.springbootplus.framework.core.pagination.BasePageOrderParam;
-import io.swagger.annotations.ApiModel;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-/**
- * <pre>
- * 角色权限关系 查询参数对象
- * </pre>
- *
- * @author geekidea
- * @date 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "SysRolePermissionPageParam对象", description = "角色权限关系查询参数")
-public class SysRolePermissionPageParam extends BasePageOrderParam {
-    private static final long serialVersionUID = 1L;
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/param/sysrole/UpdateSysRolePermissionParam.java b/system/src/main/java/io/geekidea/springbootplus/system/param/sysrole/UpdateSysRolePermissionParam.java
deleted file mode 100644
index 5abcd2e9..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/param/sysrole/UpdateSysRolePermissionParam.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.param.sysrole;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-
-import javax.validation.constraints.NotNull;
-import java.io.Serializable;
-import java.util.List;
-
-/**
- * @author geekidea
- * @date 2020/3/2
- **/
-@Data
-@ApiModel("修改系统角色权限参数")
-public class UpdateSysRolePermissionParam implements Serializable {
-
-    private static final long serialVersionUID = -672108684986772098L;
-
-    @ApiModelProperty("角色ID")
-    @NotNull(message = "角色ID不能为空")
-    private Long roleId;
-
-    @ApiModelProperty("权限ID集合")
-    private List<Long> permissionIds;
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/param/sysuser/ResetPasswordParam.java b/system/src/main/java/io/geekidea/springbootplus/system/param/sysuser/ResetPasswordParam.java
deleted file mode 100644
index 58d2b642..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/param/sysuser/ResetPasswordParam.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.param.sysuser;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import javax.validation.constraints.NotEmpty;
-import javax.validation.constraints.NotNull;
-import java.io.Serializable;
-
-/**
- * 管理员重置用户密码参数
- *
- * @author geekidea
- * @date 2020-3-8
- **/
-@Data
-@Accessors(chain = true)
-@ApiModel("管理员重置用户密码参数")
-public class ResetPasswordParam implements Serializable {
-
-    private static final long serialVersionUID = 5364321420976152005L;
-
-    @ApiModelProperty("用户id")
-    @NotNull(message = "用户id不能为空")
-    private Long userId;
-
-    @ApiModelProperty("新密码")
-    @NotEmpty(message = "新密码不能为空")
-    private String newPassword;
-
-    @ApiModelProperty("新密码")
-    @NotEmpty(message = "确认密码不能为空")
-    private String confirmPassword;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/param/sysuser/SysUserPageParam.java b/system/src/main/java/io/geekidea/springbootplus/system/param/sysuser/SysUserPageParam.java
deleted file mode 100644
index ce500026..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/param/sysuser/SysUserPageParam.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.param.sysuser;
-
-import io.geekidea.springbootplus.framework.core.pagination.BasePageOrderParam;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
-import java.util.Date;
-
-/**
- * <pre>
- * 系统用户 查询参数对象
- * </pre>
- *
- * @author geekidea
- * @date 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@EqualsAndHashCode(callSuper = true)
-@ApiModel(value = "SysUserPageParam对象", description = "系统用户查询参数")
-public class SysUserPageParam extends BasePageOrderParam {
-
-    private static final long serialVersionUID = 7437881671144580610L;
-
-    @ApiModelProperty("部门id")
-    private Long departmentId;
-
-    @ApiModelProperty("角色id")
-    private Long roleId;
-
-    @ApiModelProperty("状态，0：禁用，1：启用，2：锁定")
-    private Integer state;
-
-    @ApiModelProperty("创建时间开始")
-    private Date createTimeStart;
-
-    @ApiModelProperty("创建时间结束")
-    private Date createTimeEnd;
-
-    @ApiModelProperty("用户名")
-    private String username;
-
-    @ApiModelProperty("昵称")
-    private String nickname;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/param/sysuser/UpdatePasswordParam.java b/system/src/main/java/io/geekidea/springbootplus/system/param/sysuser/UpdatePasswordParam.java
deleted file mode 100644
index a6c5286c..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/param/sysuser/UpdatePasswordParam.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.param.sysuser;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import javax.validation.constraints.NotEmpty;
-import javax.validation.constraints.NotNull;
-import java.io.Serializable;
-
-/**
- * 修改密码参数
- *
- * @author geekidea
- * @date 2019-10-27
- **/
-@Data
-@Accessors(chain = true)
-@ApiModel("修改密码参数")
-public class UpdatePasswordParam implements Serializable {
-
-    private static final long serialVersionUID = -186284285725426339L;
-
-    @ApiModelProperty("用户id")
-    @NotNull(message = "用户id不能为空")
-    private Long userId;
-
-    @ApiModelProperty("原密码")
-    @NotEmpty(message = "原密码不能为空")
-    private String oldPassword;
-
-    @ApiModelProperty("新密码")
-    @NotEmpty(message = "新密码不能为空")
-    private String newPassword;
-
-    @ApiModelProperty("新密码")
-    @NotEmpty(message = "确认密码不能为空")
-    private String confirmPassword;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/param/sysuser/UploadHeadParam.java b/system/src/main/java/io/geekidea/springbootplus/system/param/sysuser/UploadHeadParam.java
deleted file mode 100644
index b19c6cd6..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/param/sysuser/UploadHeadParam.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.param.sysuser;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import java.io.Serializable;
-
-/**
- * 上传头像参数
- *
- * @author geekidea
- * @date 2020/3/7
- **/
-@Data
-@Accessors(chain = true)
-@ApiModel("上传头像参数")
-public class UploadHeadParam implements Serializable {
-
-    private static final long serialVersionUID = -6871175837435010592L;
-
-    @ApiModelProperty("用户ID")
-    @NotNull(message = "用户ID不能为空")
-    private Long id;
-
-    @ApiModelProperty("头像路径")
-    @NotBlank(message = "头像不能为空")
-    private String head;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/service/LoginService.java b/system/src/main/java/io/geekidea/springbootplus/system/service/LoginService.java
deleted file mode 100644
index ec2c2c26..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/service/LoginService.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.service;
-
-import io.geekidea.springbootplus.system.entity.SysUser;
-import io.geekidea.springbootplus.system.param.LoginParam;
-import io.geekidea.springbootplus.system.vo.LoginSysUserTokenVo;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * <p>
- * 登录服务接口
- * </p>
- *
- * @author geekidea
- * @date 2019-05-23
- **/
-public interface LoginService {
-
-    /**
-     * 登录
-     *
-     * @param loginParam
-     * @return
-     * @throws Exception
-     */
-    LoginSysUserTokenVo login(LoginParam loginParam) throws Exception;
-
-
-    /**
-     * 退出
-     * @param request
-     * @throws Exception
-     */
-    void logout(HttpServletRequest request) throws Exception;
-
-    /**
-     * 根据用户名获取系统用户对象
-     *
-     * @param username
-     * @return
-     * @throws Exception
-     */
-    SysUser getSysUserByUsername(String username) throws Exception;
-
-    /**
-     * 检查验证码是否正确
-     *
-     * @param verifyToken
-     * @param code
-     * @throws Exception
-     */
-    void checkVerifyCode(String verifyToken, String code) throws Exception;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/service/SysDepartmentService.java b/system/src/main/java/io/geekidea/springbootplus/system/service/SysDepartmentService.java
deleted file mode 100644
index e1eb4585..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/service/SysDepartmentService.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.service;
-
-import io.geekidea.springbootplus.framework.common.service.BaseService;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.system.entity.SysDepartment;
-import io.geekidea.springbootplus.system.param.SysDepartmentPageParam;
-import io.geekidea.springbootplus.system.vo.SysDepartmentQueryVo;
-import io.geekidea.springbootplus.system.vo.SysDepartmentTreeVo;
-
-import java.io.Serializable;
-import java.util.List;
-
-/**
- * <pre>
- * 部门 服务类
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-public interface SysDepartmentService extends BaseService<SysDepartment> {
-
-    /**
-     * 保存
-     *
-     * @param sysDepartment
-     * @return
-     * @throws Exception
-     */
-    boolean saveSysDepartment(SysDepartment sysDepartment) throws Exception;
-
-    /**
-     * 修改
-     *
-     * @param sysDepartment
-     * @return
-     * @throws Exception
-     */
-    boolean updateSysDepartment(SysDepartment sysDepartment) throws Exception;
-
-    /**
-     * 删除
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    boolean deleteSysDepartment(Long id) throws Exception;
-
-    /**
-     * 根据ID获取查询对象
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    SysDepartmentQueryVo getSysDepartmentById(Serializable id) throws Exception;
-
-    /**
-     * 获取分页对象
-     *
-     * @param sysDepartmentPageParam
-     * @return
-     * @throws Exception
-     */
-    Paging<SysDepartmentQueryVo> getSysDepartmentPageList(SysDepartmentPageParam sysDepartmentPageParam) throws Exception;
-
-    /**
-     * 根据id校验部门是否存在并且已启用
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    boolean isEnableSysDepartment(Long id) throws Exception;
-
-    /**
-     * 获取所有可用的部门列表
-     * @return
-     */
-    List<SysDepartment> getAllDepartmentList();
-
-    /**
-     * 获取所有可用的部门树形列表
-     * @return
-     */
-    List<SysDepartmentTreeVo> getDepartmentTree();
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/service/SysPermissionService.java b/system/src/main/java/io/geekidea/springbootplus/system/service/SysPermissionService.java
deleted file mode 100644
index c675185d..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/service/SysPermissionService.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.service;
-
-import io.geekidea.springbootplus.framework.common.service.BaseService;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.system.entity.SysPermission;
-import io.geekidea.springbootplus.system.param.SysPermissionPageParam;
-import io.geekidea.springbootplus.system.vo.SysPermissionQueryVo;
-import io.geekidea.springbootplus.system.vo.SysPermissionTreeVo;
-
-import java.io.Serializable;
-import java.util.List;
-
-/**
- * <pre>
- * 系统权限 服务类
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-public interface SysPermissionService extends BaseService<SysPermission> {
-
-    /**
-     * 保存
-     *
-     * @param sysPermission
-     * @return
-     * @throws Exception
-     */
-    boolean saveSysPermission(SysPermission sysPermission) throws Exception;
-
-    /**
-     * 修改
-     *
-     * @param sysPermission
-     * @return
-     * @throws Exception
-     */
-    boolean updateSysPermission(SysPermission sysPermission) throws Exception;
-
-    /**
-     * 删除
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    boolean deleteSysPermission(Long id) throws Exception;
-
-    /**
-     * 根据ID获取查询对象
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    SysPermissionQueryVo getSysPermissionById(Serializable id) throws Exception;
-
-    /**
-     * 获取分页对象
-     *
-     * @param sysPermissionPageParam
-     * @return
-     * @throws Exception
-     */
-    Paging<SysPermissionQueryVo> getSysPermissionPageList(SysPermissionPageParam sysPermissionPageParam) throws Exception;
-
-    /**
-     * 判断权限id是否存在
-     *
-     * @param permissionIds
-     * @return
-     * @throws Exception
-     */
-    boolean isExistsByPermissionIds(List<Long> permissionIds) throws Exception;
-
-    /**
-     * 获取所有菜单列表
-     *
-     * @return
-     * @throws Exception
-     */
-    List<SysPermission> getAllMenuList() throws Exception;
-
-    /**
-     * 转换权限列表为树形菜单
-     *
-     * @param sysPermissions
-     * @return
-     * @throws Exception
-     */
-    List<SysPermissionTreeVo> convertSysPermissionTreeVoList(List<SysPermission> sysPermissions) throws Exception;
-
-    /**
-     * 获取获取菜单树形列表
-     *
-     * @return
-     * @throws Exception
-     */
-    List<SysPermissionTreeVo> getAllMenuTree() throws Exception;
-
-    /**
-     * 根据用户id获取该用户所有权限编码
-     *
-     * @param userId
-     * @return
-     * @throws Exception
-     */
-    List<String> getPermissionCodesByUserId(Long userId) throws Exception;
-
-    /**
-     * 根据用户id获取菜单列表
-     *
-     * @param userId
-     * @return
-     * @throws Exception
-     */
-    List<SysPermission> getMenuListByUserId(Long userId) throws Exception;
-
-    /**
-     * 根据用户id获取菜单树形列表
-     *
-     * @param userId
-     * @return
-     * @throws Exception
-     */
-    List<SysPermissionTreeVo> getMenuTreeByUserId(Long userId) throws Exception;
-
-    /**
-     * 根据角色id获取该对应的所有三级权限ID
-     *
-     * @param roleId
-     * @return
-     * @throws Exception
-     */
-    List<Long> getPermissionIdsByRoleId(Long roleId) throws Exception;
-
-
-    /**
-     * 获取所有导航菜单(一级/二级菜单)
-     *
-     * @return
-     * @throws Exception
-     */
-    List<SysPermissionTreeVo> getNavMenuTree() throws Exception;
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/service/SysRolePermissionService.java b/system/src/main/java/io/geekidea/springbootplus/system/service/SysRolePermissionService.java
deleted file mode 100644
index f385e8f2..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/service/SysRolePermissionService.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.service;
-
-import io.geekidea.springbootplus.framework.common.service.BaseService;
-import io.geekidea.springbootplus.system.entity.SysRolePermission;
-import org.apache.commons.collections4.SetUtils;
-
-import java.util.List;
-import java.util.Set;
-
-/**
- * <pre>
- * 角色权限关系 服务类
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-public interface SysRolePermissionService extends BaseService<SysRolePermission> {
-
-    /**
-     * 保存
-     *
-     * @param roleId
-     * @param permissionIds
-     * @return
-     * @throws Exception
-     */
-    boolean saveSysRolePermission(Long roleId, List<Long> permissionIds) throws Exception;
-
-    /**
-     * 根据角色id获取权限id列表
-     *
-     * @param roleId
-     * @return
-     * @throws Exception
-     */
-    List<Long> getPermissionIdsByRoleId(Long roleId) throws Exception;
-
-    /**
-     * 根据角色id获取该对应的所有三级权限ID
-     *
-     * @param roleId
-     * @return
-     * @throws Exception
-     */
-    List<Long> getThreeLevelPermissionIdsByRoleId(Long roleId) throws Exception;
-
-    /**
-     * 批量保存角色权限关系
-     *
-     * @param roleId
-     * @param addSet
-     * @return
-     * @throws Exception
-     */
-    boolean saveSysRolePermissionBatch(Long roleId, SetUtils.SetView addSet) throws Exception;
-
-    /**
-     * 根据角色id删除关联的权限关系
-     *
-     * @param roleId
-     * @return
-     * @throws Exception
-     */
-    boolean deleteSysRolePermissionByRoleId(Long roleId) throws Exception;
-
-    /**
-     * 根据角色id获取可用的权限编码
-     *
-     * @param roleId
-     * @return
-     * @throws Exception
-     */
-    Set<String> getPermissionCodesByRoleId(Long roleId) throws Exception;
-
-    /**
-     * 通过角色id判断在角色权限表中是否有数据存在
-     *
-     * @param permissionId
-     * @return
-     * @throws Exception
-     */
-    boolean isExistsByPermissionId(Long permissionId) throws Exception;
-
-    /**
-     * 角色下是否有权限
-     *
-     * @param roleId
-     * @return
-     * @throws Exception
-     */
-    boolean hasPermission(Long roleId) throws Exception;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/service/SysRoleService.java b/system/src/main/java/io/geekidea/springbootplus/system/service/SysRoleService.java
deleted file mode 100644
index abb74800..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/service/SysRoleService.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.service;
-
-import io.geekidea.springbootplus.framework.common.service.BaseService;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.system.entity.SysRole;
-import io.geekidea.springbootplus.system.param.sysrole.SysRolePageParam;
-import io.geekidea.springbootplus.system.param.sysrole.UpdateSysRolePermissionParam;
-import io.geekidea.springbootplus.system.vo.SysRoleQueryVo;
-
-import java.io.Serializable;
-
-/**
- * <pre>
- * 系统角色 服务类
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-public interface SysRoleService extends BaseService<SysRole> {
-
-    /**
-     * 保存
-     *
-     * @param addSysRoleParam
-     * @return
-     * @throws Exception
-     */
-    boolean saveSysRole(SysRole sysRole) throws Exception;
-
-    /**
-     * 修改
-     *
-     * @param updateSysRoleParam
-     * @return
-     * @throws Exception
-     */
-    boolean updateSysRole(SysRole sysRole) throws Exception;
-
-    /**
-     * 删除
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    boolean deleteSysRole(Long id) throws Exception;
-
-    /**
-     * 根据ID获取查询对象
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    SysRoleQueryVo getSysRoleById(Serializable id) throws Exception;
-
-    /**
-     * 获取分页对象
-     *
-     * @param sysRolePageParam
-     * @return
-     * @throws Exception
-     */
-    Paging<SysRole> getSysRolePageList(SysRolePageParam sysRolePageParam) throws Exception;
-
-    /**
-     * 根据id校验角色是否存在并且已启用
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    boolean isEnableSysRole(Long id) throws Exception;
-
-    /**
-     * 判断角色编码是否存在
-     *
-     * @param code
-     * @return
-     * @throws Exception
-     */
-    boolean isExistsByCode(String code) throws Exception;
-
-    /**
-     * 修改系统角色权限配置
-     *
-     * @param param
-     * @return
-     * @throws Exception
-     */
-    boolean updateSysRolePermission(UpdateSysRolePermissionParam param) throws Exception;
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/service/SysUserService.java b/system/src/main/java/io/geekidea/springbootplus/system/service/SysUserService.java
deleted file mode 100644
index e13f44c7..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/service/SysUserService.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.service;
-
-import io.geekidea.springbootplus.framework.common.service.BaseService;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.system.entity.SysUser;
-import io.geekidea.springbootplus.system.param.sysuser.ResetPasswordParam;
-import io.geekidea.springbootplus.system.param.sysuser.SysUserPageParam;
-import io.geekidea.springbootplus.system.param.sysuser.UpdatePasswordParam;
-import io.geekidea.springbootplus.system.vo.SysUserQueryVo;
-
-import java.io.Serializable;
-
-/**
- * <pre>
- * 系统用户 服务类
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-public interface SysUserService extends BaseService<SysUser> {
-
-    /**
-     * 保存
-     *
-     * @param sysUser
-     * @return
-     * @throws Exception
-     */
-    boolean saveSysUser(SysUser sysUser) throws Exception;
-
-    /**
-     * 修改
-     *
-     * @param sysUser
-     * @return
-     * @throws Exception
-     */
-    boolean updateSysUser(SysUser sysUser) throws Exception;
-
-    /**
-     * 删除
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    boolean deleteSysUser(Long id) throws Exception;
-
-    /**
-     * 根据ID获取查询对象
-     *
-     * @param id
-     * @return
-     * @throws Exception
-     */
-    SysUserQueryVo getSysUserById(Serializable id) throws Exception;
-
-    /**
-     * 获取分页对象
-     *
-     * @param sysUserPageParam
-     * @return
-     * @throws Exception
-     */
-    Paging<SysUserQueryVo> getSysUserPageList(SysUserPageParam sysUserPageParam) throws Exception;
-
-    /**
-     * 判断用户名是否存在
-     *
-     * @param username
-     * @return
-     * @throws Exception
-     */
-    boolean isExistsByUsername(String username) throws Exception;
-
-    /**
-     * 检验部门和角色是否存在并且已启用
-     *
-     * @param departmentId
-     * @param roleId
-     * @throws Exception
-     */
-    void checkDepartmentAndRole(Long departmentId, Long roleId) throws Exception;
-
-    /**
-     * 通过角色id判断是否存在可用用户id
-     *
-     * @param roleId
-     * @return
-     * @throws Exception
-     */
-    boolean isExistsSysUserByRoleId(Long roleId) throws Exception;
-
-    /**
-     * 修改密码
-     *
-     * @param updatePasswordParam
-     * @return
-     * @throws Exception
-     */
-    boolean updatePassword(UpdatePasswordParam updatePasswordParam) throws Exception;
-
-    /**
-     * 修改系统用户头像
-     *
-     * @param id
-     * @param headPath
-     * @return
-     * @throws Exception
-     */
-    boolean updateSysUserHead(Long id, String headPath) throws Exception;
-
-    /**
-     * 管理员重置用户密码
-     *
-     * @param resetPasswordParam
-     * @return
-     * @throws Exception
-     */
-    boolean resetPassword(ResetPasswordParam resetPasswordParam) throws Exception;
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/service/impl/LoginServiceImpl.java b/system/src/main/java/io/geekidea/springbootplus/system/service/impl/LoginServiceImpl.java
deleted file mode 100644
index c0c565a4..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/service/impl/LoginServiceImpl.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import io.geekidea.springbootplus.config.constant.CommonRedisKey;
-import io.geekidea.springbootplus.config.properties.JwtProperties;
-import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties;
-import io.geekidea.springbootplus.framework.shiro.cache.LoginRedisService;
-import io.geekidea.springbootplus.framework.shiro.jwt.JwtToken;
-import io.geekidea.springbootplus.framework.shiro.util.JwtTokenUtil;
-import io.geekidea.springbootplus.framework.shiro.util.JwtUtil;
-import io.geekidea.springbootplus.framework.shiro.util.SaltUtil;
-import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
-import io.geekidea.springbootplus.framework.util.PasswordUtil;
-import io.geekidea.springbootplus.system.convert.SysUserConvert;
-import io.geekidea.springbootplus.system.entity.SysDepartment;
-import io.geekidea.springbootplus.system.entity.SysRole;
-import io.geekidea.springbootplus.system.entity.SysUser;
-import io.geekidea.springbootplus.system.enums.StateEnum;
-import io.geekidea.springbootplus.system.exception.VerificationCodeException;
-import io.geekidea.springbootplus.system.mapper.SysUserMapper;
-import io.geekidea.springbootplus.system.param.LoginParam;
-import io.geekidea.springbootplus.system.service.LoginService;
-import io.geekidea.springbootplus.system.service.SysDepartmentService;
-import io.geekidea.springbootplus.system.service.SysRolePermissionService;
-import io.geekidea.springbootplus.system.service.SysRoleService;
-import io.geekidea.springbootplus.system.vo.LoginSysUserTokenVo;
-import io.swagger.annotations.Api;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.shiro.SecurityUtils;
-import org.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.subject.Subject;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import javax.servlet.http.HttpServletRequest;
-import java.time.Duration;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-/**
- * <p>
- * 登录服务实现类
- * </p>
- *
- * @author geekidea
- * @date 2019-05-23
- **/
-@Api
-@Slf4j
-@Service
-public class LoginServiceImpl implements LoginService {
-
-    @Lazy
-    @Autowired
-    private LoginRedisService loginRedisService;
-
-    @Lazy
-    @Autowired
-    private JwtProperties jwtProperties;
-
-    @Lazy
-    @Autowired
-    private SysUserMapper sysUserMapper;
-
-    @Lazy
-    @Autowired
-    private SysDepartmentService sysDepartmentService;
-
-    @Lazy
-    @Autowired
-    private SysRoleService sysRoleService;
-
-    @Lazy
-    @Autowired
-    private SysRolePermissionService sysRolePermissionService;
-
-    @Lazy
-    @Autowired
-    private SpringBootPlusProperties springBootPlusProperties;
-
-    @Lazy
-    @Autowired
-    private RedisTemplate redisTemplate;
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public LoginSysUserTokenVo login(LoginParam loginParam) throws Exception {
-        // 校验验证码
-        checkVerifyCode(loginParam.getVerifyToken(), loginParam.getCode());
-
-        String username = loginParam.getUsername();
-        // 从数据库中获取登录用户信息
-        SysUser sysUser = getSysUserByUsername(username);
-        if (sysUser == null) {
-            log.error("登录失败,loginParam:{}", loginParam);
-            throw new AuthenticationException("用户名或密码错误");
-        }
-        if (StateEnum.DISABLE.getCode().equals(sysUser.getState())) {
-            throw new AuthenticationException("账号已禁用");
-        }
-
-        // 实际项目中，前端传过来的密码应先加密
-        // 原始密码明文：123456
-        // 原始密码前端加密：sha256(123456)
-        // 后台加密规则：sha256(sha256(123456) + salt)
-        String encryptPassword = PasswordUtil.encrypt(loginParam.getPassword(), sysUser.getSalt());
-        if (!encryptPassword.equals(sysUser.getPassword())) {
-            throw new AuthenticationException("用户名或密码错误");
-        }
-
-        // 将系统用户对象转换成登录用户对象
-        LoginSysUserVo loginSysUserVo = SysUserConvert.INSTANCE.sysUserToLoginSysUserVo(sysUser);
-
-        // 获取部门
-        SysDepartment sysDepartment = sysDepartmentService.getById(sysUser.getDepartmentId());
-        if (sysDepartment == null) {
-            throw new AuthenticationException("部门不存在");
-        }
-        if (!StateEnum.ENABLE.getCode().equals(sysDepartment.getState())) {
-            throw new AuthenticationException("部门已禁用");
-        }
-        loginSysUserVo.setDepartmentId(sysDepartment.getId())
-                .setDepartmentName(sysDepartment.getName());
-
-        // 获取当前用户角色
-        Long roleId = sysUser.getRoleId();
-        SysRole sysRole = sysRoleService.getById(roleId);
-        if (sysRole == null) {
-            throw new AuthenticationException("角色不存在");
-        }
-        if (StateEnum.DISABLE.getCode().equals(sysRole.getState())) {
-            throw new AuthenticationException("角色已禁用");
-        }
-        loginSysUserVo.setRoleId(sysRole.getId())
-                .setRoleName(sysRole.getName())
-                .setRoleCode(sysRole.getCode());
-
-        // 获取当前用户权限
-        Set<String> permissionCodes = sysRolePermissionService.getPermissionCodesByRoleId(roleId);
-        if (CollectionUtils.isEmpty(permissionCodes)) {
-            throw new AuthenticationException("权限列表不能为空");
-        }
-        loginSysUserVo.setPermissionCodes(permissionCodes);
-
-        // 获取数据库中保存的盐值
-        String newSalt = SaltUtil.getSalt(sysUser.getSalt(), jwtProperties);
-
-        // 生成token字符串并返回
-        Long expireSecond = jwtProperties.getExpireSecond();
-        String token = JwtUtil.generateToken(username, newSalt, Duration.ofSeconds(expireSecond));
-        log.debug("token:{}", token);
-
-        // 创建AuthenticationToken
-        JwtToken jwtToken = JwtToken.build(token, username, newSalt, expireSecond);
-
-        boolean enableShiro = springBootPlusProperties.getShiro().isEnable();
-        if (enableShiro) {
-            // 从SecurityUtils里边创建一个 subject
-            Subject subject = SecurityUtils.getSubject();
-            // 执行认证登录
-            subject.login(jwtToken);
-        } else {
-            log.warn("未启用Shiro");
-        }
-
-        // 缓存登录信息到Redis
-        loginRedisService.cacheLoginInfo(jwtToken, loginSysUserVo);
-        log.debug("登录成功,username:{}", username);
-
-        // 缓存登录信息到redis
-        String tokenSha256 = DigestUtils.sha256Hex(token);
-        redisTemplate.opsForValue().set(tokenSha256, loginSysUserVo, 1, TimeUnit.DAYS);
-
-        // 返回token和登录用户信息对象
-        LoginSysUserTokenVo loginSysUserTokenVo = new LoginSysUserTokenVo();
-        loginSysUserTokenVo.setToken(token);
-        loginSysUserTokenVo.setLoginSysUserVo(loginSysUserVo);
-        return loginSysUserTokenVo;
-    }
-
-    @Override
-    public void checkVerifyCode(String verifyToken, String code) throws Exception {
-        // 如果没有启用验证码，则返回
-        if (!springBootPlusProperties.isEnableVerifyCode()) {
-            return;
-        }
-        // 校验验证码
-        if (StringUtils.isBlank(code)) {
-            throw new VerificationCodeException("请输入验证码");
-        }
-        // 从redis中获取
-        String redisKey = String.format(CommonRedisKey.VERIFY_CODE, verifyToken);
-        String generateCode = (String) redisTemplate.opsForValue().get(redisKey);
-        if (StringUtils.isBlank(generateCode)) {
-            throw new VerificationCodeException("验证码已过期或不正确");
-        }
-        // 不区分大小写
-        if (!generateCode.equalsIgnoreCase(code)) {
-            throw new VerificationCodeException("验证码错误");
-        }
-        // 验证码校验成功，删除Redis缓存
-        redisTemplate.delete(redisKey);
-    }
-
-    @Override
-    public void logout(HttpServletRequest request) throws Exception {
-        Subject subject = SecurityUtils.getSubject();
-        //注销
-        subject.logout();
-        // 获取token
-        String token = JwtTokenUtil.getToken(request);
-        String username = JwtUtil.getUsername(token);
-        // 删除Redis缓存信息
-        loginRedisService.deleteLoginInfo(token, username);
-        log.info("登出成功,username:{},token:{}", username, token);
-    }
-
-    @Override
-    public SysUser getSysUserByUsername(String username) throws Exception {
-        SysUser sysUser = new SysUser().setUsername(username);
-        return sysUserMapper.selectOne(new QueryWrapper<SysUser>(sysUser));
-    }
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysDepartmentServiceImpl.java b/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysDepartmentServiceImpl.java
deleted file mode 100644
index 4158965c..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysDepartmentServiceImpl.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
-import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.system.convert.SysDepartmentConvert;
-import io.geekidea.springbootplus.system.entity.SysDepartment;
-import io.geekidea.springbootplus.system.enums.StateEnum;
-import io.geekidea.springbootplus.system.mapper.SysDepartmentMapper;
-import io.geekidea.springbootplus.system.param.SysDepartmentPageParam;
-import io.geekidea.springbootplus.system.service.SysDepartmentService;
-import io.geekidea.springbootplus.system.vo.SysDepartmentQueryVo;
-import io.geekidea.springbootplus.system.vo.SysDepartmentTreeVo;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.collections.CollectionUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-
-/**
- * <pre>
- * 部门 服务实现类
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Slf4j
-@Service
-public class SysDepartmentServiceImpl extends BaseServiceImpl<SysDepartmentMapper, SysDepartment> implements SysDepartmentService {
-
-    @Autowired
-    private SysDepartmentMapper sysDepartmentMapper;
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean saveSysDepartment(SysDepartment sysDepartment) throws Exception {
-        sysDepartment.setId(null);
-        return super.save(sysDepartment);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean updateSysDepartment(SysDepartment sysDepartment) throws Exception {
-        return super.updateById(sysDepartment);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean deleteSysDepartment(Long id) throws Exception {
-        return super.removeById(id);
-    }
-
-    @Override
-    public SysDepartmentQueryVo getSysDepartmentById(Serializable id) throws Exception {
-        return sysDepartmentMapper.getSysDepartmentById(id);
-    }
-
-    @Override
-    public Paging<SysDepartmentQueryVo> getSysDepartmentPageList(SysDepartmentPageParam sysDepartmentPageParam) throws Exception {
-        Page<SysDepartmentQueryVo> page = new PageInfo<>(sysDepartmentPageParam, OrderItem.desc(getLambdaColumn(SysDepartment::getCreateTime)));
-        IPage<SysDepartmentQueryVo> iPage = sysDepartmentMapper.getSysDepartmentPageList(page, sysDepartmentPageParam);
-        return new Paging(iPage);
-    }
-
-    @Override
-    public boolean isEnableSysDepartment(Long id) throws Exception {
-        SysDepartment sysDepartment = new SysDepartment()
-                .setId(id)
-                .setState(StateEnum.ENABLE.getCode());
-        int count = sysDepartmentMapper.selectCount(new QueryWrapper<>(sysDepartment));
-        return count > 0;
-    }
-
-    @Override
-    public List<SysDepartment> getAllDepartmentList() {
-        SysDepartment sysDepartment = new SysDepartment().setState(StateEnum.ENABLE.getCode());
-        // 获取所有已启用的部门列表
-        return sysDepartmentMapper.selectList(new QueryWrapper(sysDepartment));
-    }
-
-    @Override
-    public List<SysDepartmentTreeVo> getDepartmentTree() {
-        List<SysDepartment> sysDepartmentList = getAllDepartmentList();
-        if (CollectionUtils.isEmpty(sysDepartmentList)) {
-            throw new IllegalArgumentException("SysDepartment列表不能为空");
-        }
-        List<SysDepartmentTreeVo> list = SysDepartmentConvert.INSTANCE.listToTreeVoList(sysDepartmentList);
-        List<SysDepartmentTreeVo> treeVos = new ArrayList<>();
-        for (SysDepartmentTreeVo treeVo : list) {
-            if (treeVo.getParentId() == null) {
-                treeVos.add(findChildren(treeVo, list));
-            }
-        }
-        return treeVos;
-    }
-
-    /**
-     * 递归获取树形结果列表
-     *
-     * @param tree
-     * @param list
-     * @return
-     */
-    public SysDepartmentTreeVo findChildren(SysDepartmentTreeVo tree, List<SysDepartmentTreeVo> list) {
-        for (SysDepartmentTreeVo vo : list) {
-            if (tree.getId().equals(vo.getParentId())) {
-                if (tree.getChildren() == null) {
-                    tree.setChildren(new ArrayList<>());
-                }
-                tree.getChildren().add(findChildren(vo, list));
-            }
-        }
-        return tree;
-    }
-
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysPermissionServiceImpl.java b/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysPermissionServiceImpl.java
deleted file mode 100644
index 00755626..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysPermissionServiceImpl.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.Wrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import io.geekidea.springbootplus.framework.common.exception.BusinessException;
-import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
-import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.system.convert.SysPermissionConvert;
-import io.geekidea.springbootplus.system.entity.SysPermission;
-import io.geekidea.springbootplus.system.enums.MenuLevelEnum;
-import io.geekidea.springbootplus.system.enums.StateEnum;
-import io.geekidea.springbootplus.system.mapper.SysPermissionMapper;
-import io.geekidea.springbootplus.system.param.SysPermissionPageParam;
-import io.geekidea.springbootplus.system.service.SysPermissionService;
-import io.geekidea.springbootplus.system.service.SysRolePermissionService;
-import io.geekidea.springbootplus.system.vo.SysPermissionQueryVo;
-import io.geekidea.springbootplus.system.vo.SysPermissionTreeVo;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.collections4.CollectionUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.io.Serializable;
-import java.util.*;
-import java.util.stream.Collectors;
-
-
-/**
- * <pre>
- * 系统权限 服务实现类
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-25
- */
-@Slf4j
-@Service
-public class SysPermissionServiceImpl extends BaseServiceImpl<SysPermissionMapper, SysPermission> implements SysPermissionService {
-
-    @Autowired
-    private SysPermissionMapper sysPermissionMapper;
-
-    @Autowired
-    private SysRolePermissionService sysRolePermissionService;
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean saveSysPermission(SysPermission sysPermission) throws Exception {
-        sysPermission.setId(null);
-        return super.save(sysPermission);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean updateSysPermission(SysPermission sysPermission) throws Exception {
-        // 获取权限
-        if (getById(sysPermission.getId()) == null) {
-            throw new BusinessException("权限不存在");
-        }
-        sysPermission.setUpdateTime(new Date());
-        return super.updateById(sysPermission);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean deleteSysPermission(Long id) throws Exception {
-        boolean isExists = sysRolePermissionService.isExistsByPermissionId(id);
-        if (isExists) {
-            throw new BusinessException("该权限存在角色关联关系，不能删除");
-        }
-        return super.removeById(id);
-    }
-
-    @Override
-    public SysPermissionQueryVo getSysPermissionById(Serializable id) throws Exception {
-        return sysPermissionMapper.getSysPermissionById(id);
-    }
-
-    @Override
-    public Paging<SysPermissionQueryVo> getSysPermissionPageList(SysPermissionPageParam sysPermissionPageParam) throws Exception {
-        Page<SysPermissionQueryVo> page = new PageInfo<>(sysPermissionPageParam, OrderItem.desc(getLambdaColumn(SysPermission::getCreateTime)));
-        IPage<SysPermissionQueryVo> iPage = sysPermissionMapper.getSysPermissionPageList(page, sysPermissionPageParam);
-        return new Paging(iPage);
-    }
-
-    @Override
-    public boolean isExistsByPermissionIds(List<Long> permissionIds) {
-        if (CollectionUtils.isEmpty(permissionIds)) {
-            return false;
-        }
-        Wrapper wrapper = lambdaQuery().in(SysPermission::getId, permissionIds).getWrapper();
-        return sysPermissionMapper.selectCount(wrapper).intValue() == permissionIds.size();
-    }
-
-    @Override
-    public List<SysPermission> getAllMenuList() throws Exception {
-        SysPermission sysPermission = new SysPermission().setState(StateEnum.ENABLE.getCode());
-        // 获取所有已启用的权限列表
-        return sysPermissionMapper.selectList(new QueryWrapper(sysPermission));
-    }
-
-    @Override
-    public List<SysPermissionTreeVo> getAllMenuTree() throws Exception {
-        List<SysPermission> list = getAllMenuList();
-        // 转换成树形菜单
-        List<SysPermissionTreeVo> treeVos = convertSysPermissionTreeVoList(list);
-        return treeVos;
-    }
-
-    @Override
-    public List<SysPermissionTreeVo> convertSysPermissionTreeVoList(List<SysPermission> list) {
-        if (CollectionUtils.isEmpty(list)) {
-            throw new IllegalArgumentException("SysPermission列表不能为空");
-        }
-        // 按level分组获取map
-        Map<Integer, List<SysPermission>> map = list.stream().collect(Collectors.groupingBy(SysPermission::getLevel));
-        List<SysPermissionTreeVo> treeVos = new ArrayList<>();
-        // 循环获取三级菜单树形集合
-        for (SysPermission one : map.get(MenuLevelEnum.ONE.getCode())) {
-            SysPermissionTreeVo oneVo = SysPermissionConvert.INSTANCE.permissionToTreeVo(one);
-            Long oneParentId = oneVo.getParentId();
-            if (oneParentId == null || oneParentId == 0) {
-                treeVos.add(oneVo);
-            }
-            List<SysPermission> twoList = map.get(MenuLevelEnum.TWO.getCode());
-            if (CollectionUtils.isNotEmpty(twoList)) {
-                for (SysPermission two : twoList) {
-                    SysPermissionTreeVo twoVo = SysPermissionConvert.INSTANCE.permissionToTreeVo(two);
-                    if (two.getParentId().equals(one.getId())) {
-                        if (oneVo.getChildren() == null) {
-                            oneVo.setChildren(new ArrayList<>());
-                        }
-                        oneVo.getChildren().add(twoVo);
-                    }
-                    List<SysPermission> threeList = map.get(MenuLevelEnum.THREE.getCode());
-                    if (CollectionUtils.isNotEmpty(threeList)) {
-                        for (SysPermission three : threeList) {
-                            if (three.getParentId().equals(two.getId())) {
-                                SysPermissionTreeVo threeVo = SysPermissionConvert.INSTANCE.permissionToTreeVo(three);
-                                if (twoVo.getChildren() == null) {
-                                    twoVo.setChildren(new ArrayList<>());
-                                }
-                                twoVo.getChildren().add(threeVo);
-                            }
-                        }
-                    }
-                }
-            }
-
-        }
-        return treeVos;
-    }
-
-    @Override
-    public List<String> getPermissionCodesByUserId(Long userId) throws Exception {
-        return sysPermissionMapper.getPermissionCodesByUserId(userId);
-    }
-
-    @Override
-    public List<SysPermission> getMenuListByUserId(Long userId) throws Exception {
-        return sysPermissionMapper.getMenuListByUserId(userId);
-    }
-
-    @Override
-    public List<SysPermissionTreeVo> getMenuTreeByUserId(Long userId) throws Exception {
-        List<SysPermission> list = getMenuListByUserId(userId);
-        // 转换成树形菜单
-        List<SysPermissionTreeVo> treeVos = convertSysPermissionTreeVoList(list);
-        return treeVos;
-    }
-
-    @Override
-    public List<Long> getPermissionIdsByRoleId(Long roleId) throws Exception {
-
-        // 根据角色id获取该对应的所有三级权限ID
-
-        return null;
-    }
-
-
-    @Override
-    public List<SysPermissionTreeVo> getNavMenuTree() throws Exception {
-        List<Integer> levels = Arrays.asList(MenuLevelEnum.ONE.getCode(), MenuLevelEnum.TWO.getCode());
-        Wrapper wrapper = lambdaQuery()
-                .in(SysPermission::getLevel, levels)
-                .eq(SysPermission::getState, StateEnum.ENABLE.getCode())
-                .getWrapper();
-
-
-        List<SysPermission> list = sysPermissionMapper.selectList(wrapper);
-
-        return convertSysPermissionTreeVoList(list);
-
-    }
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysRolePermissionServiceImpl.java b/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysRolePermissionServiceImpl.java
deleted file mode 100644
index d96b029b..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysRolePermissionServiceImpl.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.Wrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
-import io.geekidea.springbootplus.system.entity.SysRolePermission;
-import io.geekidea.springbootplus.system.enums.StateEnum;
-import io.geekidea.springbootplus.system.mapper.SysRoleMapper;
-import io.geekidea.springbootplus.system.mapper.SysRolePermissionMapper;
-import io.geekidea.springbootplus.system.service.SysRolePermissionService;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.collections4.SetUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-
-/**
- * <pre>
- * 角色权限关系 服务实现类
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-25
- */
-@Slf4j
-@Service
-public class SysRolePermissionServiceImpl extends BaseServiceImpl<SysRolePermissionMapper, SysRolePermission> implements SysRolePermissionService {
-
-    @Autowired
-    private SysRolePermissionMapper sysRolePermissionMapper;
-
-    @Autowired
-    private SysRoleMapper sysRoleMapper;
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean saveSysRolePermission(Long roleId, List<Long> permissionIds) throws Exception {
-        List<SysRolePermission> list = new ArrayList<>();
-        permissionIds.forEach(permissionId -> {
-            SysRolePermission sysRolePermission = new SysRolePermission()
-                    .setRoleId(roleId)
-                    .setPermissionId(permissionId)
-                    .setState(StateEnum.ENABLE.getCode());
-            list.add(sysRolePermission);
-        });
-        // 批量保存角色权限中间表
-        return saveBatch(list, 20);
-    }
-
-    @Override
-    public List<Long> getPermissionIdsByRoleId(Long roleId) throws Exception {
-        Wrapper wrapper = lambdaQuery()
-                .select(SysRolePermission::getPermissionId)
-                .eq(SysRolePermission::getRoleId,roleId)
-                .eq(SysRolePermission::getState,StateEnum.ENABLE.getCode())
-                .getWrapper();
-        return sysRolePermissionMapper.selectObjs(wrapper);
-    }
-
-    @Override
-    public List<Long> getThreeLevelPermissionIdsByRoleId(Long roleId) throws Exception {
-        return sysRolePermissionMapper.getThreeLevelPermissionIdsByRoleId(roleId);
-    }
-
-    @Override
-    public boolean saveSysRolePermissionBatch(Long roleId, SetUtils.SetView addSet) {
-        List<SysRolePermission> list = new ArrayList<>();
-        addSet.forEach(id -> {
-            SysRolePermission sysRolePermission = new SysRolePermission();
-            Long permissionId = (Long) id;
-            sysRolePermission
-                    .setRoleId(roleId)
-                    .setPermissionId(permissionId)
-                    .setState(StateEnum.ENABLE.getCode());
-            list.add(sysRolePermission);
-        });
-        return saveBatch(list, 20);
-    }
-
-    @Override
-    public boolean deleteSysRolePermissionByRoleId(Long roleId) throws Exception {
-        SysRolePermission sysRolePermission = new SysRolePermission()
-                .setRoleId(roleId);
-        return remove(new QueryWrapper<>(sysRolePermission));
-    }
-
-    @Override
-    public Set<String> getPermissionCodesByRoleId(Long roleId) throws Exception {
-        return sysRolePermissionMapper.getPermissionCodesByRoleId(roleId);
-    }
-
-    @Override
-    public boolean isExistsByPermissionId(Long permissionId) throws Exception {
-        // 判断角色权限表是否有关联存在，如果存在，则不能删除
-        SysRolePermission sysRolePermission = new SysRolePermission()
-                .setPermissionId(permissionId);
-        return count(new QueryWrapper<SysRolePermission>(sysRolePermission)) > 0;
-    }
-
-    @Override
-    public boolean hasPermission(Long roleId) throws Exception {
-        SysRolePermission sysRolePermission = new SysRolePermission()
-                .setRoleId(roleId);
-        return count(new QueryWrapper<SysRolePermission>(sysRolePermission)) > 0;
-    }
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysRoleServiceImpl.java b/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysRoleServiceImpl.java
deleted file mode 100644
index 5406c918..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysRoleServiceImpl.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import io.geekidea.springbootplus.framework.common.exception.BusinessException;
-import io.geekidea.springbootplus.framework.common.exception.DaoException;
-import io.geekidea.springbootplus.framework.common.exception.SpringBootPlusException;
-import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
-import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.system.entity.SysRole;
-import io.geekidea.springbootplus.system.entity.SysRolePermission;
-import io.geekidea.springbootplus.system.enums.StateEnum;
-import io.geekidea.springbootplus.system.mapper.SysRoleMapper;
-import io.geekidea.springbootplus.system.param.sysrole.SysRolePageParam;
-import io.geekidea.springbootplus.system.param.sysrole.UpdateSysRolePermissionParam;
-import io.geekidea.springbootplus.system.service.SysPermissionService;
-import io.geekidea.springbootplus.system.service.SysRolePermissionService;
-import io.geekidea.springbootplus.system.service.SysRoleService;
-import io.geekidea.springbootplus.system.service.SysUserService;
-import io.geekidea.springbootplus.system.vo.SysRoleQueryVo;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.SetUtils;
-import org.apache.commons.collections4.SetUtils.SetView;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.io.Serializable;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-
-/**
- * <pre>
- * 系统角色 服务实现类
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Slf4j
-@Service
-public class SysRoleServiceImpl extends BaseServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {
-
-    @Autowired
-    private SysRoleMapper sysRoleMapper;
-
-    @Autowired
-    private SysPermissionService sysPermissionService;
-
-    @Autowired
-    private SysRolePermissionService sysRolePermissionService;
-
-    @Autowired
-    private SysUserService sysUserService;
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean saveSysRole(SysRole sysRole) throws Exception {
-        String code = sysRole.getCode();
-        // 校验角色标识code唯一性
-        if (isExistsByCode(code)) {
-            throw new BusinessException("角色编码已存在");
-        }
-        // 保存角色
-        boolean saveRoleResult = super.save(sysRole);
-        if (!saveRoleResult) {
-            throw new DaoException("保存角色失败");
-        }
-        return true;
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean updateSysRole(SysRole sysRole) throws Exception {
-        Long roleId = sysRole.getId();
-        // 校验角色是否存在
-        if (getById(roleId) == null) {
-            throw new BusinessException("该角色不存在");
-        }
-        // 修改角色
-        sysRole.setUpdateTime(new Date());
-        boolean updateResult = updateById(sysRole);
-        if (!updateResult) {
-            throw new DaoException("修改系统角色失败");
-        }
-        return true;
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean deleteSysRole(Long id) throws Exception {
-        // 判断该角色下是否有可用用户，如果有，则不能删除
-        boolean isExistsUser = sysUserService.isExistsSysUserByRoleId(id);
-        if (isExistsUser) {
-            throw new DaoException("该角色下还存在可用用户，不能删除");
-        }
-        // 角色真实删除
-        boolean deleteRoleResult = removeById(id);
-        if (!deleteRoleResult) {
-            throw new DaoException("删除角色失败");
-        }
-
-        // 判断角色是否有权限，如果有，则删除
-        boolean hasPermission = sysRolePermissionService.hasPermission(id);
-        if (hasPermission) {
-            // 角色权限关系真实删除
-            boolean deletePermissionResult = sysRolePermissionService.deleteSysRolePermissionByRoleId(id);
-            if (!deletePermissionResult) {
-                throw new DaoException("删除角色权限关系失败");
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public SysRoleQueryVo getSysRoleById(Serializable id) throws Exception {
-        SysRoleQueryVo sysRoleQueryVo = sysRoleMapper.getSysRoleById(id);
-        if (sysRoleQueryVo == null) {
-            throw new SpringBootPlusException("角色不存在");
-        }
-        List<Long> permissionIds = sysRolePermissionService.getPermissionIdsByRoleId((Long) id);
-        sysRoleQueryVo.setPermissions(new HashSet<>(permissionIds));
-        return sysRoleQueryVo;
-    }
-
-    @Override
-    public Paging<SysRole> getSysRolePageList(SysRolePageParam sysRolePageParam) throws Exception {
-        Page<SysRole> page = new PageInfo<>(sysRolePageParam, OrderItem.desc(getLambdaColumn(SysRole::getCreateTime)));
-        // 此处演示单表，使用mybatisplus自带方法进行分页
-        LambdaQueryWrapper<SysRole> lambdaQueryWrapper = new LambdaQueryWrapper<>();
-        String keyword = sysRolePageParam.getKeyword();
-        String name = sysRolePageParam.getName();
-        String code = sysRolePageParam.getCode();
-        Integer state = sysRolePageParam.getState();
-        if (StringUtils.isNotBlank(keyword)) {
-            lambdaQueryWrapper
-                    .like(SysRole::getName, keyword)
-                    .or()
-                    .like(SysRole::getCode, keyword);
-        }
-        if (StringUtils.isNotBlank(name)) {
-            lambdaQueryWrapper.like(SysRole::getName, name);
-        }
-        if (StringUtils.isNotBlank(code)) {
-            lambdaQueryWrapper.like(SysRole::getCode, code);
-        }
-        if (state != null) {
-            lambdaQueryWrapper.eq(SysRole::getState, state);
-        }
-        IPage<SysRole> iPage = sysRoleMapper.selectPage(page, lambdaQueryWrapper);
-        return new Paging<SysRole>(iPage);
-    }
-
-    @Override
-    public boolean isEnableSysRole(Long id) throws Exception {
-        SysRole sysRole = new SysRole()
-                .setId(id)
-                .setState(StateEnum.ENABLE.getCode());
-        int count = sysRoleMapper.selectCount(new QueryWrapper<>(sysRole));
-        return count > 0;
-    }
-
-    @Override
-    public boolean isExistsByCode(String code) throws Exception {
-        SysRole sysRole = new SysRole().setCode(code);
-        return sysRoleMapper.selectCount(new QueryWrapper<>(sysRole)) > 0;
-    }
-
-    @Override
-    public boolean updateSysRolePermission(UpdateSysRolePermissionParam param) throws Exception {
-        Long roleId = param.getRoleId();
-        List<Long> permissionIds = param.getPermissionIds();
-        // 校验角色是否存在
-        SysRole sysRole = getById(roleId);
-        if (sysRole == null) {
-            throw new BusinessException("该角色不存在");
-        }
-        if (CollectionUtils.isNotEmpty(permissionIds)) {
-            // 校验权限列表是否存在
-            if (!sysPermissionService.isExistsByPermissionIds(permissionIds)) {
-                throw new BusinessException("权限列表id匹配失败");
-            }
-        }
-        // 获取之前的权限id集合
-        List<Long> beforeList = sysRolePermissionService.getPermissionIdsByRoleId(roleId);
-        // 差集计算
-        // before：1,2,3,4,5,6
-        // after： 1,2,3,4,7,8
-        // 删除5,6 新增7,8
-        // 此处真实删除，去掉deleted字段的@TableLogic注解
-        Set<Long> beforeSet = new HashSet<>(beforeList);
-        Set<Long> afterSet = new HashSet<>(permissionIds);
-        SetView<Long> deleteSet = SetUtils.difference(beforeSet, afterSet);
-        SetView<Long> addSet = SetUtils.difference(afterSet, beforeSet);
-        log.debug("deleteSet = " + deleteSet);
-        log.debug("addSet = " + addSet);
-
-        if (CollectionUtils.isNotEmpty(deleteSet)) {
-            // 删除权限关联
-            LambdaUpdateWrapper<SysRolePermission> updateWrapper = new LambdaUpdateWrapper<>();
-            updateWrapper.eq(SysRolePermission::getRoleId, roleId);
-            updateWrapper.in(SysRolePermission::getPermissionId, deleteSet);
-            boolean deleteResult = sysRolePermissionService.remove(updateWrapper);
-            if (!deleteResult) {
-                throw new DaoException("删除角色权限关系失败");
-            }
-        }
-
-        if (CollectionUtils.isNotEmpty(addSet)) {
-            // 新增权限关联
-            boolean addResult = sysRolePermissionService.saveSysRolePermissionBatch(roleId, addSet);
-            if (!addResult) {
-                throw new DaoException("新增角色权限关系失败");
-            }
-        }
-
-        return true;
-    }
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysUserServiceImpl.java b/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysUserServiceImpl.java
deleted file mode 100644
index de366695..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/service/impl/SysUserServiceImpl.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.service.impl;
-
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.metadata.OrderItem;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import io.geekidea.springbootplus.config.properties.SpringBootPlusProperties;
-import io.geekidea.springbootplus.framework.common.exception.BusinessException;
-import io.geekidea.springbootplus.framework.common.service.impl.BaseServiceImpl;
-import io.geekidea.springbootplus.framework.core.pagination.PageInfo;
-import io.geekidea.springbootplus.framework.core.pagination.Paging;
-import io.geekidea.springbootplus.framework.shiro.util.SaltUtil;
-import io.geekidea.springbootplus.framework.util.PasswordUtil;
-import io.geekidea.springbootplus.framework.util.PhoneUtil;
-import io.geekidea.springbootplus.system.entity.SysUser;
-import io.geekidea.springbootplus.system.enums.StateEnum;
-import io.geekidea.springbootplus.system.mapper.SysUserMapper;
-import io.geekidea.springbootplus.system.param.sysuser.ResetPasswordParam;
-import io.geekidea.springbootplus.system.param.sysuser.SysUserPageParam;
-import io.geekidea.springbootplus.system.param.sysuser.UpdatePasswordParam;
-import io.geekidea.springbootplus.system.service.SysDepartmentService;
-import io.geekidea.springbootplus.system.service.SysRoleService;
-import io.geekidea.springbootplus.system.service.SysUserService;
-import io.geekidea.springbootplus.system.vo.SysUserQueryVo;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.io.Serializable;
-import java.util.Date;
-
-
-/**
- * <pre>
- * 系统用户 服务实现类
- * </pre>
- *
- * @author geekidea
- * @since 2019-10-24
- */
-@Slf4j
-@Service
-public class SysUserServiceImpl extends BaseServiceImpl<SysUserMapper, SysUser> implements SysUserService {
-
-    @Autowired
-    private SysUserMapper sysUserMapper;
-
-    @Autowired
-    private SysDepartmentService sysDepartmentService;
-
-    @Lazy
-    @Autowired
-    private SysRoleService sysRoleService;
-
-    @Autowired
-    private SpringBootPlusProperties springBootPlusProperties;
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean saveSysUser(SysUser sysUser) throws Exception {
-        // 校验用户名是否存在
-        boolean isExists = isExistsByUsername(sysUser.getUsername());
-        if (isExists) {
-            throw new BusinessException("用户名已存在");
-        }
-        // 校验部门和角色
-        checkDepartmentAndRole(sysUser.getDepartmentId(), sysUser.getRoleId());
-        sysUser.setId(null);
-
-        // 生成盐值
-        String salt = null;
-        String password = sysUser.getPassword();
-        // 如果密码为空，则设置默认密码
-        if (StringUtils.isBlank(password)) {
-            salt = springBootPlusProperties.getLoginInitSalt();
-            password = springBootPlusProperties.getLoginInitPassword();
-        } else {
-            salt = SaltUtil.generateSalt();
-        }
-        // 密码加密
-        sysUser.setSalt(salt);
-        sysUser.setPassword(PasswordUtil.encrypt(password, salt));
-
-        // 如果头像为空，则设置默认头像
-        if (StringUtils.isBlank(sysUser.getHead())) {
-            sysUser.setHead(springBootPlusProperties.getLoginInitHead());
-        }
-
-        // 保存系统用户
-        return super.save(sysUser);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean updateSysUser(SysUser sysUser) throws Exception {
-        // 校验部门和角色
-        checkDepartmentAndRole(sysUser.getDepartmentId(), sysUser.getRoleId());
-        // 获取系统用户
-        SysUser updateSysUser = getById(sysUser.getId());
-        if (updateSysUser == null) {
-            throw new BusinessException("修改的用户不存在");
-        }
-
-        // 修改系统用户
-        updateSysUser.setNickname(sysUser.getNickname())
-                .setPhone(sysUser.getPhone())
-                .setGender(sysUser.getGender())
-                .setRemark(sysUser.getRemark())
-                .setState(sysUser.getState())
-                .setDepartmentId(sysUser.getDepartmentId())
-                .setRoleId(sysUser.getRoleId())
-                .setUpdateTime(new Date());
-        return super.updateById(updateSysUser);
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    @Override
-    public boolean deleteSysUser(Long id) throws Exception {
-        return super.removeById(id);
-    }
-
-    @Override
-    public SysUserQueryVo getSysUserById(Serializable id) throws Exception {
-        return sysUserMapper.getSysUserById(id);
-    }
-
-    @Override
-    public Paging<SysUserQueryVo> getSysUserPageList(SysUserPageParam sysUserPageParam) throws Exception {
-        Page<SysUserQueryVo> page = new PageInfo<>(sysUserPageParam, OrderItem.desc(getLambdaColumn(SysUser::getCreateTime)));
-        IPage<SysUserQueryVo> iPage = sysUserMapper.getSysUserPageList(page, sysUserPageParam);
-
-        // 手机号码脱敏处理
-        if (iPage != null && CollectionUtils.isNotEmpty(iPage.getRecords())) {
-            iPage.getRecords().forEach(vo -> {
-                vo.setPhone(PhoneUtil.desensitize(vo.getPhone()));
-            });
-        }
-
-        return new Paging(iPage);
-    }
-
-    @Override
-    public boolean isExistsByUsername(String username) throws Exception {
-        SysUser selectSysUser = new SysUser().setUsername(username);
-        return sysUserMapper.selectCount(new QueryWrapper<>(selectSysUser)) > 0;
-    }
-
-    @Override
-    public void checkDepartmentAndRole(Long departmentId, Long roleId) throws Exception {
-        // 校验部门是否存在并且可用
-        boolean isEnableDepartment = sysDepartmentService.isEnableSysDepartment(departmentId);
-        if (!isEnableDepartment) {
-            throw new BusinessException("该部门不存在或已禁用");
-        }
-        // 校验角色是否存在并且可用
-        boolean isEnableRole = sysRoleService.isEnableSysRole(roleId);
-        if (!isEnableRole) {
-            throw new BusinessException("该角色不存在或已禁用");
-        }
-    }
-
-    @Override
-    public boolean isExistsSysUserByRoleId(Long roleId) throws Exception {
-        SysUser sysUser = new SysUser()
-                .setState(StateEnum.ENABLE.getCode())
-                .setRoleId(roleId);
-        return sysUserMapper.selectCount(new QueryWrapper(sysUser)) > 0;
-    }
-
-    @Override
-    public boolean updatePassword(UpdatePasswordParam updatePasswordParam) throws Exception {
-        String oldPassword = updatePasswordParam.getOldPassword();
-        String newPassword = updatePasswordParam.getNewPassword();
-        String confirmPassword = updatePasswordParam.getConfirmPassword();
-        if (!newPassword.equals(confirmPassword)) {
-            throw new BusinessException("两次输入的密码不一致");
-        }
-        if (newPassword.equals(oldPassword)) {
-            throw new BusinessException("新密码和旧密码不能一致");
-        }
-
-        // 判断原密码是否正确
-        SysUser sysUser = getById(updatePasswordParam.getUserId());
-        if (sysUser == null) {
-            throw new BusinessException("用户不存在");
-        }
-        if (StateEnum.DISABLE.getCode().equals(sysUser.getState())) {
-            throw new BusinessException("用户已禁用");
-        }
-        // 密码加密处理
-        String salt = sysUser.getSalt();
-        String encryptOldPassword = PasswordUtil.encrypt(oldPassword, salt);
-        if (!sysUser.getPassword().equals(encryptOldPassword)) {
-            throw new BusinessException("原密码错误");
-        }
-        // 新密码加密
-        String encryptNewPassword = PasswordUtil.encrypt(newPassword, salt);
-
-        // 修改密码
-        SysUser updateSysUser = new SysUser()
-                .setId(sysUser.getId())
-                .setPassword(encryptNewPassword)
-                .setUpdateTime(new Date());
-        return updateById(updateSysUser);
-    }
-
-    @Override
-    public boolean updateSysUserHead(Long id, String headPath) throws Exception {
-        SysUser sysUser = new SysUser()
-                .setId(id)
-                .setHead(headPath);
-        return updateById(sysUser);
-    }
-
-    @Override
-    public boolean resetPassword(ResetPasswordParam resetPasswordParam) throws Exception {
-        String newPassword = resetPasswordParam.getNewPassword();
-        String confirmPassword = resetPasswordParam.getConfirmPassword();
-        if (!newPassword.equals(confirmPassword)) {
-            throw new BusinessException("两次输入的密码不一致");
-        }
-        // 判断用户是否可修改
-        SysUser sysUser = getById(resetPasswordParam.getUserId());
-        if (sysUser == null) {
-            throw new BusinessException("用户不存在");
-        }
-        if (StateEnum.DISABLE.getCode().equals(sysUser.getState())) {
-            throw new BusinessException("用户已禁用");
-        }
-        // 密码加密处理
-        String salt = sysUser.getSalt();
-        // 新密码加密
-        String encryptNewPassword = PasswordUtil.encrypt(newPassword, salt);
-
-        // 修改密码
-        SysUser updateSysUser = new SysUser()
-                .setId(sysUser.getId())
-                .setPassword(encryptNewPassword)
-                .setUpdateTime(new Date());
-        return updateById(updateSysUser);
-    }
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/vo/LoginSysUserTokenVo.java b/system/src/main/java/io/geekidea/springbootplus/system/vo/LoginSysUserTokenVo.java
deleted file mode 100644
index 5a454fac..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/vo/LoginSysUserTokenVo.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.vo;
-
-import io.geekidea.springbootplus.framework.shiro.service.LoginToken;
-import io.geekidea.springbootplus.framework.shiro.vo.LoginSysUserVo;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-/**
- * @author geekidea
- * @date 2019-10-26
- **/
-@Data
-@Accessors(chain = true)
-@ApiModel("登录用户信息TokenVO")
-public class LoginSysUserTokenVo implements LoginToken {
-
-    private static final long serialVersionUID = -2138450422989081056L;
-
-    @ApiModelProperty("token")
-    private String token;
-
-    /**
-     * 登录用户对象
-     */
-    private LoginSysUserVo loginSysUserVo;
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysDepartmentQueryVo.java b/system/src/main/java/io/geekidea/springbootplus/system/vo/SysDepartmentQueryVo.java
deleted file mode 100644
index b43a175e..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysDepartmentQueryVo.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.vo;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Date;
-
-/**
- * <pre>
- * 部门 查询结果对象
- * </pre>
- *
- * @author geekidea
- * @date 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@ApiModel(value = "SysDepartmentQueryVo对象", description = "部门查询参数")
-public class SysDepartmentQueryVo implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty("主键")
-    private Long id;
-
-    @ApiModelProperty("部门名称")
-    private String name;
-
-    @ApiModelProperty("父id")
-    private Long parentId;
-
-    @ApiModelProperty("状态，0：禁用，1：启用")
-    private Integer state;
-
-    @ApiModelProperty("排序")
-    private Integer sort;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("版本")
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    private Date updateTime;
-
-}
\ No newline at end of file
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysDepartmentTreeVo.java b/system/src/main/java/io/geekidea/springbootplus/system/vo/SysDepartmentTreeVo.java
deleted file mode 100644
index 58e4c2ff..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysDepartmentTreeVo.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.vo;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Date;
-import java.util.List;
-
-/**
- * <pre>
- * 部门TreeVo
- * </pre>
- *
- * @author geekidea
- * @since 2019-11-1
- */
-@Data
-@Accessors(chain = true)
-@ApiModel(value = "SysDepartmentTreeVo对象", description = "部门")
-public class SysDepartmentTreeVo implements Serializable {
-    private static final long serialVersionUID = -2250233632748939400L;
-
-    @ApiModelProperty("主键")
-    private Long id;
-
-    @ApiModelProperty("部门名称")
-    private String name;
-
-    @ApiModelProperty("父id")
-    private Long parentId;
-
-    @ApiModelProperty("状态，0：禁用，1：启用")
-    private Integer state;
-
-    @ApiModelProperty("排序")
-    private Integer sort;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("版本")
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    private Date updateTime;
-
-    private List<SysDepartmentTreeVo> children;
-
-}
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysPermissionQueryVo.java b/system/src/main/java/io/geekidea/springbootplus/system/vo/SysPermissionQueryVo.java
deleted file mode 100644
index bdf4601e..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysPermissionQueryVo.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.vo;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Date;
-
-/**
- * <pre>
- * 系统权限 查询结果对象
- * </pre>
- *
- * @author geekidea
- * @date 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@ApiModel(value = "SysPermissionQueryVo对象", description = "系统权限查询参数")
-public class SysPermissionQueryVo implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty("主键")
-    private Long id;
-
-    @ApiModelProperty("权限名称")
-    private String name;
-
-    @ApiModelProperty("父id")
-    private Long parentId;
-
-    @ApiModelProperty("路径")
-    private String url;
-
-    @ApiModelProperty("唯一编码")
-    private String code;
-
-    @ApiModelProperty("图标")
-    private String icon;
-
-    @ApiModelProperty("类型，1：菜单，2：按钮")
-    private Integer type;
-
-    @ApiModelProperty("层级，1：第一级，2：第二级，N：第N级")
-    private Integer level;
-
-    @ApiModelProperty("状态，0：禁用，1：启用")
-    private Integer state;
-
-    @ApiModelProperty("排序")
-    private Integer sort;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("版本")
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    private Date updateTime;
-
-}
\ No newline at end of file
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysPermissionTreeVo.java b/system/src/main/java/io/geekidea/springbootplus/system/vo/SysPermissionTreeVo.java
deleted file mode 100644
index e31395c3..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysPermissionTreeVo.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.vo;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Date;
-import java.util.List;
-
-/**
- * 系统权限树形列表VO
- *
- * @author geekidea
- * @date 2019-10-26
- **/
-@Data
-@Accessors(chain = true)
-@ApiModel(value = "SysPermissionTreeVo对象", description = "系统权限树形列表")
-public class SysPermissionTreeVo implements Serializable {
-    private static final long serialVersionUID = 2738804574228359190L;
-
-    @ApiModelProperty("主键")
-    private Long id;
-
-    @ApiModelProperty("权限名称")
-    private String name;
-
-    @ApiModelProperty("父id")
-    private Long parentId;
-
-    @ApiModelProperty("路径")
-    private String url;
-
-    @ApiModelProperty("唯一编码")
-    private String code;
-
-    @ApiModelProperty("图标")
-    private String icon;
-
-    @ApiModelProperty("类型，1：菜单，2：按钮")
-    private Integer type;
-
-    @ApiModelProperty("层级，1：第一级，2：第二级，N：第N级")
-    private Integer level;
-
-    @ApiModelProperty("状态，0：禁用，1：启用")
-    private Integer state;
-
-    @ApiModelProperty("排序")
-    private Integer sort;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("版本")
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    private Date updateTime;
-
-    @ApiModelProperty("子节点集合")
-    private List<SysPermissionTreeVo> children;
-
-}
\ No newline at end of file
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysRolePermissionQueryVo.java b/system/src/main/java/io/geekidea/springbootplus/system/vo/SysRolePermissionQueryVo.java
deleted file mode 100644
index 1a7feb9c..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysRolePermissionQueryVo.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.vo;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Date;
-
-/**
- * <pre>
- * 角色权限关系 查询结果对象
- * </pre>
- *
- * @author geekidea
- * @date 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@ApiModel(value = "SysRolePermissionQueryVo对象", description = "角色权限关系查询参数")
-public class SysRolePermissionQueryVo implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty("主键")
-    private Long id;
-
-    @ApiModelProperty("角色id")
-    private Long roleId;
-
-    @ApiModelProperty("权限id")
-    private Long permissionId;
-
-    @ApiModelProperty("状态，0：禁用，1：启用")
-    private Integer state;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("版本")
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    private Date updateTime;
-
-}
\ No newline at end of file
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysRoleQueryVo.java b/system/src/main/java/io/geekidea/springbootplus/system/vo/SysRoleQueryVo.java
deleted file mode 100644
index 6b56cc5a..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysRoleQueryVo.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.vo;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Date;
-import java.util.Set;
-
-/**
- * <pre>
- * 系统角色 查询结果对象
- * </pre>
- *
- * @author geekidea
- * @date 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@ApiModel(value = "SysRoleQueryVo对象", description = "系统角色查询参数")
-public class SysRoleQueryVo implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty("主键")
-    private Long id;
-
-    @ApiModelProperty("角色名称")
-    private String name;
-
-    @ApiModelProperty("角色唯一编码")
-    private String code;
-
-    @ApiModelProperty("角色类型")
-    private Integer type;
-
-    @ApiModelProperty("角色状态，0：禁用，1：启用")
-    private Integer state;
-
-    @ApiModelProperty("备注")
-    private String remark;
-
-    @ApiModelProperty("版本")
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    private Date updateTime;
-
-    @ApiModelProperty("权限集合")
-    private Set<Long> permissions;
-
-}
\ No newline at end of file
diff --git a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysUserQueryVo.java b/system/src/main/java/io/geekidea/springbootplus/system/vo/SysUserQueryVo.java
deleted file mode 100644
index 61d12bc2..00000000
--- a/system/src/main/java/io/geekidea/springbootplus/system/vo/SysUserQueryVo.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2019-2029 geekidea(https://github.com/geekidea)
- *
- * 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.
- */
-
-package io.geekidea.springbootplus.system.vo;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.io.Serializable;
-import java.util.Date;
-
-/**
- * <pre>
- * 系统用户 查询结果对象
- * </pre>
- *
- * @author geekidea
- * @date 2019-10-24
- */
-@Data
-@Accessors(chain = true)
-@ApiModel(value = "SysUserQueryVo对象", description = "系统用户查询参数")
-public class SysUserQueryVo implements Serializable {
-    private static final long serialVersionUID = 1L;
-
-    @ApiModelProperty("主键")
-    private Long id;
-
-    @ApiModelProperty("用户名")
-    private String username;
-
-    @ApiModelProperty("昵称")
-    private String nickname;
-
-    @ApiModelProperty("手机号码")
-    private String phone;
-
-    @ApiModelProperty("性别，0：女，1：男，默认1")
-    private Integer gender;
-
-    @ApiModelProperty("头像")
-    private String head;
-
-    @ApiModelProperty("remark")
-    private String remark;
-
-    @ApiModelProperty("状态，0：禁用，1：启用，2：锁定")
-    private Integer state;
-
-    @ApiModelProperty("部门id")
-    private Long departmentId;
-
-    @ApiModelProperty("角色id")
-    private Long roleId;
-
-    @ApiModelProperty("逻辑删除，0：未删除，1：已删除")
-    private Integer deleted;
-
-    @ApiModelProperty("版本")
-    private Integer version;
-
-    @ApiModelProperty("创建时间")
-    private Date createTime;
-
-    @ApiModelProperty("修改时间")
-    private Date updateTime;
-
-    @ApiModelProperty("部门名称")
-    private String departmentName;
-
-    @ApiModelProperty("角色名称")
-    private String roleName;
-
-}
\ No newline at end of file
diff --git a/system/src/main/resources/mapper/system.md b/system/src/main/resources/mapper/system.md
deleted file mode 100644
index 57a223c1..00000000
--- a/system/src/main/resources/mapper/system.md
+++ /dev/null
@@ -1 +0,0 @@
-# 系统模块Mapper XML
\ No newline at end of file
diff --git a/system/src/main/resources/mapper/system/SysDepartmentMapper.xml b/system/src/main/resources/mapper/system/SysDepartmentMapper.xml
deleted file mode 100644
index 538436de..00000000
--- a/system/src/main/resources/mapper/system/SysDepartmentMapper.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="io.geekidea.springbootplus.system.mapper.SysDepartmentMapper">
-
-    <!-- 通用查询结果列 -->
-    <sql id="Base_Column_List">
-        id, name, parent_id, state, sort, remark, version, create_time, update_time
-    </sql>
-
-    <select id="getSysDepartmentById" resultType="io.geekidea.springbootplus.system.vo.SysDepartmentQueryVo">
-        select
-        <include refid="Base_Column_List"/>
-        from sys_department where id = #{id}
-    </select>
-
-    <select id="getSysDepartmentPageList" resultType="io.geekidea.springbootplus.system.vo.SysDepartmentQueryVo">
-        select
-        <include refid="Base_Column_List"/>
-        from sys_department
-    </select>
-
-</mapper>
diff --git a/system/src/main/resources/mapper/system/SysPermissionMapper.xml b/system/src/main/resources/mapper/system/SysPermissionMapper.xml
deleted file mode 100644
index f60b935a..00000000
--- a/system/src/main/resources/mapper/system/SysPermissionMapper.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="io.geekidea.springbootplus.system.mapper.SysPermissionMapper">
-
-    <!-- 通用查询结果列 -->
-    <sql id="Base_Column_List">
-        id, name, parent_id, url, code, icon, type, level, state, sort, remark, version, create_time, update_time
-    </sql>
-
-    <select id="getSysPermissionById" resultType="io.geekidea.springbootplus.system.vo.SysPermissionQueryVo">
-        select
-        <include refid="Base_Column_List"/>
-        from sys_permission where id = #{id}
-    </select>
-
-    <select id="getSysPermissionPageList" resultType="io.geekidea.springbootplus.system.vo.SysPermissionQueryVo">
-        select
-        <include refid="Base_Column_List"/>
-        from sys_permission
-    </select>
-
-    <select id="getPermissionCodesByUserId" resultType="java.lang.String">
-        select p.code
-        from sys_user u
-                 inner join sys_role r
-                            on u.role_id = r.id
-                 inner join sys_role_permission rp
-                            on r.id = rp.role_id
-                 inner join sys_permission p
-                            on rp.permission_id = p.id
-        where u.state = 1
-          and r.state = 1
-          and rp.state = 1
-          and p.state = 1
-          and u.deleted = 0
-          and u.id = #{userId};
-    </select>
-
-    <select id="getMenuListByUserId" resultType="io.geekidea.springbootplus.system.entity.SysPermission">
-        select p.*
-        from sys_user u
-                 inner join sys_role r
-                            on u.role_id = r.id
-                 inner join sys_role_permission rp
-                            on r.id = rp.role_id
-                 inner join sys_permission p
-                            on rp.permission_id = p.id
-        where u.state = 1
-          and u.deleted = 0
-          and r.state = 1
-          and rp.state = 1
-          and p.state = 1
-          and u.id = #{userId}
-    </select>
-
-</mapper>
diff --git a/system/src/main/resources/mapper/system/SysRoleMapper.xml b/system/src/main/resources/mapper/system/SysRoleMapper.xml
deleted file mode 100644
index ea23291d..00000000
--- a/system/src/main/resources/mapper/system/SysRoleMapper.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="io.geekidea.springbootplus.system.mapper.SysRoleMapper">
-
-    <!-- 通用查询结果列 -->
-    <sql id="Base_Column_List">
-        id, name, code, type, state, remark, version, create_time, update_time
-    </sql>
-
-    <select id="getSysRoleById" resultType="io.geekidea.springbootplus.system.vo.SysRoleQueryVo">
-        select
-        <include refid="Base_Column_List"/>
-        from sys_role where id = #{id}
-    </select>
-
-</mapper>
diff --git a/system/src/main/resources/mapper/system/SysRolePermissionMapper.xml b/system/src/main/resources/mapper/system/SysRolePermissionMapper.xml
deleted file mode 100644
index 7d802dc4..00000000
--- a/system/src/main/resources/mapper/system/SysRolePermissionMapper.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="io.geekidea.springbootplus.system.mapper.SysRolePermissionMapper">
-
-    <!-- 通用查询结果列 -->
-    <sql id="Base_Column_List">
-        id, role_id, permission_id, state, remark, version, create_time, update_time
-    </sql>
-
-    <select id="getSysRolePermissionById" resultType="io.geekidea.springbootplus.system.vo.SysRolePermissionQueryVo">
-        select
-        <include refid="Base_Column_List"/>
-        from sys_role_permission where id = #{id}
-    </select>
-
-    <select id="getSysRolePermissionPageList"
-            resultType="io.geekidea.springbootplus.system.vo.SysRolePermissionQueryVo">
-        select
-        <include refid="Base_Column_List"/>
-        from sys_role_permission
-    </select>
-
-    <select id="getPermissionCodesByRoleId" resultType="java.lang.String">
-        select p.code
-        from sys_role r
-                 inner join sys_role_permission rp
-                            on r.id = rp.role_id
-                 inner join sys_permission p
-                            on rp.permission_id = p.id
-        where r.state = 1
-          and rp.state = 1
-          and p.state = 1
-          and r.id = #{roleId};
-    </select>
-
-    <select id="getThreeLevelPermissionIdsByRoleId" resultType="long">
-        select distinct rp.permission_id
-        from sys_role r
-        inner join sys_role_permission rp on r.id = rp.role_id
-        inner join sys_permission p on rp.permission_id = p.id
-        where r.id = #{roleId} and p.level = 3 and r.state = 1 and rp.state = 1 and p.state = 1
-    </select>
-
-</mapper>
diff --git a/system/src/main/resources/mapper/system/SysUserMapper.xml b/system/src/main/resources/mapper/system/SysUserMapper.xml
deleted file mode 100644
index 1578d9de..00000000
--- a/system/src/main/resources/mapper/system/SysUserMapper.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Copyright 2019-2029 geekidea(https://github.com/geekidea)
-  ~
-  ~ 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.
-  -->
-
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
-<mapper namespace="io.geekidea.springbootplus.system.mapper.SysUserMapper">
-
-    <!-- 通用查询结果列 -->
-    <sql id="Base_Column_List">
-        id, username, nickname, phone, gender, head, remark, state, department_id, role_id, deleted, version, create_time, update_time
-    </sql>
-
-    <sql id="BaseQuerySelect">
-        select
-            u.id, u.username, u.nickname, u.phone, u.gender, u.head, u.remark,
-            u.state, u.department_id, u.role_id, u.deleted, u.create_time, u.update_time,
-            d.name as departmentName,
-            r.name as roleName
-        from sys_user u
-        inner join sys_department d on u.department_id = d.id
-        inner join sys_role r on u.role_id = r.id
-        where u.deleted = 0
-    </sql>
-
-    <select id="getSysUserById" resultType="io.geekidea.springbootplus.system.vo.SysUserQueryVo">
-        <include refid="BaseQuerySelect"/>
-        and u.id = #{id}
-    </select>
-
-    <select id="getSysUserPageList" resultType="io.geekidea.springbootplus.system.vo.SysUserQueryVo">
-        <include refid="BaseQuerySelect"/>
-        <if test="param.departmentId != null">
-            and u.department_id = #{param.departmentId}
-        </if>
-        <if test="param.roleId != null">
-            and u.role_id = #{param.roleId}
-        </if>
-        <if test="param.state != null">
-            and u.state = #{param.state}
-        </if>
-        <if test="param.createTimeStart != null">
-            and date_format(u.create_time,'%Y-%m-%d') >= date_format(#{param.createTimeStart},'%Y-%m-%d')
-        </if>
-        <if test="param.createTimeEnd != null">
-            and date_format(u.create_time,'%Y-%m-%d') &lt;= date_format(#{param.createTimeEnd},'%Y-%m-%d')
-        </if>
-        <if test="param.keyword != null and param.keyword != ''">
-            and (
-            u.username like concat('%', #{param.keyword} ,'%') or
-            u.nickname like concat('%', #{param.keyword} ,'%')
-            )
-        </if>
-        <if test="param.username != null and param.username != ''">
-            and u.username like concat('%', #{param.username} ,'%')
-        </if>
-        <if test="param.nickname != null and param.nickname != ''">
-            and u.nickname like concat('%', #{param.nickname} ,'%')
-        </if>
-    </select>
-
-</mapper>
diff --git "a/\344\270\232\345\212\241\345\222\250\350\257\242\345\225\206\345\212\241\345\220\210\344\275\234\351\241\271\347\233\256\345\244\226\345\214\205.md" "b/\344\270\232\345\212\241\345\222\250\350\257\242\345\225\206\345\212\241\345\220\210\344\275\234\351\241\271\347\233\256\345\244\226\345\214\205.md"
new file mode 100644
index 00000000..e246bbf0
--- /dev/null
+++ "b/\344\270\232\345\212\241\345\222\250\350\257\242\345\225\206\345\212\241\345\220\210\344\275\234\351\241\271\347\233\256\345\244\226\345\214\205.md"
@@ -0,0 +1,9 @@
+# 四川极客创想科技有限公司
+## 软件定制开发、框架定制开发、项目外包合作
+## 软件定制开发、框架定制开发、项目外包合作
+## 软件定制开发、框架定制开发、项目外包合作
+### 商城开发、小程序开发、APP开发、物联网开发、SASS系统开发等等
+
+### 添加企业微信咨询
+![添加企业微信](https://geekidea.oss-cn-chengdu.aliyuncs.com/geekidea/business-card.png)
+
diff --git "a/\344\274\201\344\270\232\345\276\256\344\277\241\344\272\244\346\265\201\347\276\244.jpeg" "b/\344\274\201\344\270\232\345\276\256\344\277\241\344\272\244\346\265\201\347\276\244.jpeg"
new file mode 100644
index 00000000..8ace3fb5
Binary files /dev/null and "b/\344\274\201\344\270\232\345\276\256\344\277\241\344\272\244\346\265\201\347\276\244.jpeg" differ
diff --git "a/\345\256\242\346\234\215\345\222\250\350\257\242.png" "b/\345\256\242\346\234\215\345\222\250\350\257\242.png"
new file mode 100644
index 00000000..539c1d1d
Binary files /dev/null and "b/\345\256\242\346\234\215\345\222\250\350\257\242.png" differ
