Browse Source

添加省份模块

gjs 4 years ago
commit
5e9e087b43
100 changed files with 5780 additions and 0 deletions
  1. 29 0
      .gitignore
  2. 201 0
      LICENSE
  3. 356 0
      README.md
  4. BIN
      images/mall_tiny_start_01.png
  5. BIN
      images/mall_tiny_start_02.png
  6. BIN
      images/mall_tiny_start_03.png
  7. BIN
      images/mall_tiny_start_04.png
  8. BIN
      images/mall_tiny_start_05.png
  9. BIN
      images/mall_tiny_start_06.png
  10. BIN
      images/mall_tiny_start_07.png
  11. BIN
      images/mall_tiny_start_08.png
  12. BIN
      images/mall_tiny_start_09.png
  13. 184 0
      pom.xml
  14. 361 0
      sql/mall_tiny.sql
  15. 13 0
      src/main/java/com/macro/mall/tiny/MallTinyApplication.java
  16. 71 0
      src/main/java/com/macro/mall/tiny/common/api/CommonPage.java
  17. 124 0
      src/main/java/com/macro/mall/tiny/common/api/CommonResult.java
  18. 11 0
      src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java
  19. 28 0
      src/main/java/com/macro/mall/tiny/common/api/ResultCode.java
  20. 68 0
      src/main/java/com/macro/mall/tiny/common/config/BaseRedisConfig.java
  21. 81 0
      src/main/java/com/macro/mall/tiny/common/config/BaseSwaggerConfig.java
  22. 47 0
      src/main/java/com/macro/mall/tiny/common/domain/SwaggerProperties.java
  23. 33 0
      src/main/java/com/macro/mall/tiny/common/exception/ApiException.java
  24. 18 0
      src/main/java/com/macro/mall/tiny/common/exception/Asserts.java
  25. 55 0
      src/main/java/com/macro/mall/tiny/common/exception/GlobalExceptionHandler.java
  26. 182 0
      src/main/java/com/macro/mall/tiny/common/service/RedisService.java
  27. 197 0
      src/main/java/com/macro/mall/tiny/common/service/impl/RedisServiceImpl.java
  28. 34 0
      src/main/java/com/macro/mall/tiny/config/GlobalCorsConfig.java
  29. 54 0
      src/main/java/com/macro/mall/tiny/config/MallSecurityConfig.java
  30. 25 0
      src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java
  31. 15 0
      src/main/java/com/macro/mall/tiny/config/RedisConfig.java
  32. 27 0
      src/main/java/com/macro/mall/tiny/config/SwaggerConfig.java
  33. 62 0
      src/main/java/com/macro/mall/tiny/domain/AdminUserDetails.java
  34. 162 0
      src/main/java/com/macro/mall/tiny/generator/MyBatisPlusGenerator.java
  35. 42 0
      src/main/java/com/macro/mall/tiny/modules/business/controller/BProvinceController.java
  36. 16 0
      src/main/java/com/macro/mall/tiny/modules/business/mapper/BProvinceMapper.java
  37. 35 0
      src/main/java/com/macro/mall/tiny/modules/business/model/BProvince.java
  38. 18 0
      src/main/java/com/macro/mall/tiny/modules/business/service/BProvinceCacheService.java
  39. 19 0
      src/main/java/com/macro/mall/tiny/modules/business/service/BProvinceService.java
  40. 39 0
      src/main/java/com/macro/mall/tiny/modules/business/service/impl/BProvinceCacheServiceImpl.java
  41. 44 0
      src/main/java/com/macro/mall/tiny/modules/business/service/impl/BProvinceServiceImpl.java
  42. 204 0
      src/main/java/com/macro/mall/tiny/modules/ums/controller/UmsAdminController.java
  43. 103 0
      src/main/java/com/macro/mall/tiny/modules/ums/controller/UmsMenuController.java
  44. 70 0
      src/main/java/com/macro/mall/tiny/modules/ums/controller/UmsResourceCategoryController.java
  45. 98 0
      src/main/java/com/macro/mall/tiny/modules/ums/controller/UmsResourceController.java
  46. 128 0
      src/main/java/com/macro/mall/tiny/modules/ums/controller/UmsRoleController.java
  47. 22 0
      src/main/java/com/macro/mall/tiny/modules/ums/dto/UmsAdminLoginParam.java
  48. 32 0
      src/main/java/com/macro/mall/tiny/modules/ums/dto/UmsAdminParam.java
  49. 19 0
      src/main/java/com/macro/mall/tiny/modules/ums/dto/UmsMenuNode.java
  50. 25 0
      src/main/java/com/macro/mall/tiny/modules/ums/dto/UpdateAdminPasswordParam.java
  51. 16 0
      src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsAdminLoginLogMapper.java
  52. 24 0
      src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsAdminMapper.java
  53. 21 0
      src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsAdminRoleRelationMapper.java
  54. 28 0
      src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsMenuMapper.java
  55. 16 0
      src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsResourceCategoryMapper.java
  56. 29 0
      src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsResourceMapper.java
  57. 24 0
      src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsRoleMapper.java
  58. 16 0
      src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsRoleMenuRelationMapper.java
  59. 19 0
      src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsRoleResourceRelationMapper.java
  60. 58 0
      src/main/java/com/macro/mall/tiny/modules/ums/model/UmsAdmin.java
  61. 44 0
      src/main/java/com/macro/mall/tiny/modules/ums/model/UmsAdminLoginLog.java
  62. 36 0
      src/main/java/com/macro/mall/tiny/modules/ums/model/UmsAdminRoleRelation.java
  63. 57 0
      src/main/java/com/macro/mall/tiny/modules/ums/model/UmsMenu.java
  64. 48 0
      src/main/java/com/macro/mall/tiny/modules/ums/model/UmsResource.java
  65. 42 0
      src/main/java/com/macro/mall/tiny/modules/ums/model/UmsResourceCategory.java
  66. 50 0
      src/main/java/com/macro/mall/tiny/modules/ums/model/UmsRole.java
  67. 38 0
      src/main/java/com/macro/mall/tiny/modules/ums/model/UmsRoleMenuRelation.java
  68. 38 0
      src/main/java/com/macro/mall/tiny/modules/ums/model/UmsRoleResourceRelation.java
  69. 59 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/UmsAdminCacheService.java
  70. 11 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/UmsAdminRoleRelationService.java
  71. 84 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/UmsAdminService.java
  72. 40 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/UmsMenuService.java
  73. 24 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/UmsResourceCategoryService.java
  74. 34 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/UmsResourceService.java
  75. 11 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/UmsRoleMenuRelationService.java
  76. 11 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/UmsRoleResourceRelationService.java
  77. 58 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/UmsRoleService.java
  78. 115 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsAdminCacheServiceImpl.java
  79. 15 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsAdminRoleRelationServiceImpl.java
  80. 267 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsAdminServiceImpl.java
  81. 94 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsMenuServiceImpl.java
  82. 32 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsResourceCategoryServiceImpl.java
  83. 62 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsResourceServiceImpl.java
  84. 15 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsRoleMenuRelationServiceImpl.java
  85. 15 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsRoleResourceRelationServiceImpl.java
  86. 116 0
      src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsRoleServiceImpl.java
  87. 12 0
      src/main/java/com/macro/mall/tiny/security/annotation/CacheException.java
  88. 50 0
      src/main/java/com/macro/mall/tiny/security/aspect/RedisCacheAspect.java
  89. 51 0
      src/main/java/com/macro/mall/tiny/security/component/DynamicAccessDecisionManager.java
  90. 77 0
      src/main/java/com/macro/mall/tiny/security/component/DynamicSecurityFilter.java
  91. 64 0
      src/main/java/com/macro/mall/tiny/security/component/DynamicSecurityMetadataSource.java
  92. 16 0
      src/main/java/com/macro/mall/tiny/security/component/DynamicSecurityService.java
  93. 57 0
      src/main/java/com/macro/mall/tiny/security/component/JwtAuthenticationTokenFilter.java
  94. 27 0
      src/main/java/com/macro/mall/tiny/security/component/RestAuthenticationEntryPoint.java
  95. 29 0
      src/main/java/com/macro/mall/tiny/security/component/RestfulAccessDeniedHandler.java
  96. 21 0
      src/main/java/com/macro/mall/tiny/security/config/IgnoreUrlsConfig.java
  97. 127 0
      src/main/java/com/macro/mall/tiny/security/config/SecurityConfig.java
  98. 170 0
      src/main/java/com/macro/mall/tiny/security/util/JwtTokenUtil.java
  99. 44 0
      src/main/java/com/macro/mall/tiny/security/util/SpringUtil.java
  100. 16 0
      src/main/resources/application-dev.yml

+ 29 - 0
.gitignore

@@ -0,0 +1,29 @@
+HELP.md
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### 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/

+ 201 - 0
LICENSE

@@ -0,0 +1,201 @@
+                                 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 [2018-2021] [macrozheng]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 356 - 0
README.md

@@ -0,0 +1,356 @@
+# mall-tiny
+
+<p>
+    <a href="#公众号"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E5%85%AC%E4%BC%97%E5%8F%B7-macrozheng-blue.svg" alt="公众号"></a>
+    <a href="#公众号"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E4%BA%A4%E6%B5%81-%E5%BE%AE%E4%BF%A1%E7%BE%A4-2BA245.svg" alt="交流"></a>    
+    <a href="https://github.com/macrozheng/mall"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E5%90%8E%E5%8F%B0%E9%A1%B9%E7%9B%AE-mall-blue.svg" alt="后台项目"></a>
+    <a href="https://github.com/macrozheng/mall-admin-web"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E5%89%8D%E7%AB%AF%E9%A1%B9%E7%9B%AE-mall--admin--web-green.svg" alt="前端项目"></a>
+</p>
+
+## 简介
+
+mall-tiny是一款基于SpringBoot+MyBatis-Plus的快速开发脚手架,拥有完整的权限管理功能,可对接Vue前端,开箱即用。
+
+## 项目演示
+
+mall-tiny项目可无缝对接`mall-admin-web`前端项目,秒变权限管理系统。前端项目地址:https://github.com/macrozheng/mall-admin-web
+
+![](http://img.macrozheng.com/mall/project/mall_tiny_start_09.png)
+
+## 技术选型
+
+| 技术                   | 版本    | 说明             |
+| ---------------------- | ------- | ---------------- |
+| SpringBoot             | 2.3.0   | 容器+MVC框架     |
+| SpringSecurity         | 5.3.2   | 认证和授权框架   |
+| MyBatis                | 3.5.4   | ORM框架          |
+| MyBatis-Plus           | 3.3.2   | MyBatis增强工具  |
+| MyBatis-Plus Generator | 3.3.2   | 数据层代码生成器 |
+| Swagger-UI             | 2.9.2   | 文档生产工具     |
+| Redis                  | 5.0     | 分布式缓存       |
+| Docker                 | 18.09.0 | 应用容器引擎     |
+| Druid                  | 1.1.10  | 数据库连接池     |
+| JWT                    | 0.9.0   | JWT登录支持      |
+| Lombok                 | 1.18.12 | 简化对象封装工具 |
+
+## 数据库表结构
+
+![](http://img.macrozheng.com/mall/project/mall_tiny_start_01.png)
+
+- 化繁为简,仅保留了权限管理功能相关的9张表,方便自由定制;
+
+- 数据库源文件地址:https://github.com/macrozheng/mall-tiny/blob/master/sql/mall_tiny.sql
+
+## 使用流程
+
+### 环境搭建
+
+简化依赖服务,只需安装最常用的MySql和Redis服务即可,服务安装具体参考[mall在Windows环境下的部署](https://mp.weixin.qq.com/s/Q9ybpfq8IEdbZmvlaMXJdg),数据库中需要导入`mall_tiny.sql`脚本。
+
+### 开发规约
+
+#### 项目包结构
+
+``` lua
+src
+├── common -- 用于存放通用代码
+|   ├── api -- 通用结果集封装类
+|   ├── config -- 通用配置类
+|   ├── domain -- 通用封装对象
+|   ├── exception -- 全局异常处理相关类
+|   └── service -- 通用业务类
+├── config -- SpringBoot中的Java配置
+├── domain -- 共用封装对象
+├── generator -- MyBatis-Plus代码生成器
+├── modules -- 存放业务代码的基础包
+|   └── ums -- 权限管理模块业务代码
+|       ├── controller -- 该模块相关接口
+|       ├── dto -- 该模块数据传输封装对象
+|       ├── mapper -- 该模块相关Mapper接口
+|       ├── model -- 该模块相关实体类
+|       └── service -- 该模块相关业务处理类
+└── security -- SpringSecurity认证授权相关代码
+    ├── annotation -- 相关注解
+    ├── aspect -- 相关切面
+    ├── component -- 认证授权相关组件
+    ├── config -- 相关配置
+    └── util -- 相关工具类
+```
+
+#### 资源文件说明
+
+``` lua
+resources
+├── mapper -- MyBatis中mapper.xml存放位置
+├── application.yml -- SpringBoot通用配置文件
+├── application-dev.yml -- SpringBoot开发环境配置文件
+├── application-prod.yml -- SpringBoot生产环境配置文件
+└── generator.properties -- MyBatis-Plus代码生成器配置
+```
+
+#### 接口定义规则
+
+- 创建表记录:POST /{控制器路由名称}/create
+
+- 修改表记录:POST /{控制器路由名称}/update/{id}
+
+- 删除指定表记录:POST /{控制器路由名称}/delete/{id}
+
+- 分页查询表记录:GET /{控制器路由名称}/list
+
+- 获取指定记录详情:GET /{控制器路由名称}/{id}
+
+- 具体参数及返回结果定义可以运行代码查看Swagger-UI的Api文档:http://localhost:8080/swagger-ui.html
+
+![](http://img.macrozheng.com/mall/project/mall_tiny_start_02.png)
+
+### 项目运行
+
+直接运行启动类`MallTinyApplication`的`main`函数即可。
+
+### 业务代码开发流程
+
+#### 创建业务表
+
+> 创建好`pms`模块的所有表,需要注意的是一定要写好表字段的`注释`,这样实体类和接口文档中就会自动生成字段说明了。
+
+![](http://img.macrozheng.com/mall/project/mall_tiny_start_03.png)
+
+#### 使用代码生成器
+
+> 运行`MyBatisPlusGenerator`类的main方法来生成代码,可直接生成controller、service、mapper、model、mapper.xml的代码,无需手动创建。
+
+- 代码生成器支持两种模式,一种生成单表的代码,比如只生成`pms_brand`表代码可以先输入`pms`,后输入`pms_brand`;
+
+![](http://img.macrozheng.com/mall/project/mall_tiny_start_04.png)
+
+- 生成代码结构一览;
+
+![](http://img.macrozheng.com/mall/project/mall_tiny_start_05.png)
+
+- 另一种直接生成整个模块的代码,比如生成`pms`模块代码可以先输入`pms`,后输入`pms_*`。
+
+![](http://img.macrozheng.com/mall/project/mall_tiny_start_06.png)
+
+#### 编写业务代码
+
+##### 单表查询
+
+> 由于MyBatis-Plus提供的增强功能相当强大,单表查询几乎不用手写SQL,直接使用ServiceImpl和BaseMapper中提供的方法即可。
+
+比如我们的菜单管理业务实现类`UmsMenuServiceImpl`中的方法都直接使用了这些方法。
+
+```java
+/**
+ * 后台菜单管理Service实现类
+ * Created by macro on 2020/2/2.
+ */
+@Service
+public class UmsMenuServiceImpl extends ServiceImpl<UmsMenuMapper,UmsMenu>implements UmsMenuService {
+
+    @Override
+    public boolean create(UmsMenu umsMenu) {
+        umsMenu.setCreateTime(new Date());
+        updateLevel(umsMenu);
+        return save(umsMenu);
+    }
+
+    @Override
+    public boolean update(Long id, UmsMenu umsMenu) {
+        umsMenu.setId(id);
+        updateLevel(umsMenu);
+        return updateById(umsMenu);
+    }
+
+    @Override
+    public Page<UmsMenu> list(Long parentId, Integer pageSize, Integer pageNum) {
+        Page<UmsMenu> page = new Page<>(pageNum,pageSize);
+        QueryWrapper<UmsMenu> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(UmsMenu::getParentId,parentId)
+                .orderByDesc(UmsMenu::getSort);
+        return page(page,wrapper);
+    }
+
+    @Override
+    public List<UmsMenuNode> treeList() {
+        List<UmsMenu> menuList = list();
+        List<UmsMenuNode> result = menuList.stream()
+                .filter(menu -> menu.getParentId().equals(0L))
+                .map(menu -> covertMenuNode(menu, menuList)).collect(Collectors.toList());
+        return result;
+    }
+
+    @Override
+    public boolean updateHidden(Long id, Integer hidden) {
+        UmsMenu umsMenu = new UmsMenu();
+        umsMenu.setId(id);
+        umsMenu.setHidden(hidden);
+        return updateById(umsMenu);
+    }
+}
+```
+
+##### 分页查询
+
+> 对于分页查询MyBatis-Plus原生支持,不需要再整合其他插件,直接构造Page对象,然后调用ServiceImpl中的page方法即可。
+
+```java
+/**
+ * 后台菜单管理Service实现类
+ * Created by macro on 2020/2/2.
+ */
+@Service
+public class UmsMenuServiceImpl extends ServiceImpl<UmsMenuMapper,UmsMenu>implements UmsMenuService {
+    @Override
+    public Page<UmsMenu> list(Long parentId, Integer pageSize, Integer pageNum) {
+        Page<UmsMenu> page = new Page<>(pageNum,pageSize);
+        QueryWrapper<UmsMenu> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(UmsMenu::getParentId,parentId)
+                .orderByDesc(UmsMenu::getSort);
+        return page(page,wrapper);
+    }
+}
+```
+
+##### 多表查询
+
+> 对于多表查询,我们需要手写mapper.xml中的SQL实现,由于之前我们已经生成了mapper.xml文件,所以我们直接在Mapper接口中定义好方法,然后在mapper.xml写好SQL实现即可。
+
+- 比如说我们需要写一个根据用户ID获取其分配的菜单的方法,首先我们在`UmsMenuMapper`接口中添加好`getMenuList`方法;
+
+```java
+/**
+ * <p>
+ * 后台菜单表 Mapper 接口
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+public interface UmsMenuMapper extends BaseMapper<UmsMenu> {
+
+    /**
+     * 根据后台用户ID获取菜单
+     */
+    List<UmsMenu> getMenuList(@Param("adminId") Long adminId);
+
+}
+```
+
+- 然后在`UmsMenuMapper.xml`添加该方法的对应SQL实现即可。
+
+```xml
+<?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="com.macro.mall.tiny.modules.ums.mapper.UmsMenuMapper">
+
+    <select id="getMenuList" resultType="com.macro.mall.tiny.modules.ums.model.UmsMenu">
+        SELECT
+            m.id id,
+            m.parent_id parentId,
+            m.create_time createTime,
+            m.title title,
+            m.level level,
+            m.sort sort,
+            m.name name,
+            m.icon icon,
+            m.hidden hidden
+        FROM
+            ums_admin_role_relation arr
+                LEFT JOIN ums_role r ON arr.role_id = r.id
+                LEFT JOIN ums_role_menu_relation rmr ON r.id = rmr.role_id
+                LEFT JOIN ums_menu m ON rmr.menu_id = m.id
+        WHERE
+            arr.admin_id = #{adminId}
+          AND m.id IS NOT NULL
+        GROUP BY
+            m.id
+    </select>
+    
+</mapper>
+```
+
+### 项目部署
+
+mall-tiny已经集成了Docker插件,可以打包成Docker镜像来部署,具体参考:[使用Maven插件为SpringBoot应用构建Docker镜像](https://mp.weixin.qq.com/s/q2KDzHbPkf3Q0EY8qYjYgw)
+
+### 其他说明
+
+#### SpringSecurity相关
+
+> 由于使用了SpringSecurity来实现认证和授权,部分接口需要token才可以访问,访问需要认证授权接口流程如下。
+
+- 访问Swagger-UI接口文档:http://localhost:8080/swagger-ui.html
+
+- 调用登录接口获取token;
+
+![](http://img.macrozheng.com/mall/project/mall_tiny_start_07.png)
+
+- 点击右上角Authorize按钮输入token,然后访问相关接口即可。
+
+![](http://img.macrozheng.com/mall/project/mall_tiny_start_08.png)
+
+#### 请求参数校验
+
+> 默认集成了`Jakarta Bean Validation`参数校验框架,只需在参数对象属性中添加`javax.validation.constraints`包中的注解注解即可实现校验功能,这里以登录参数校验为例。
+
+- 首先在登录请求参数中添加`@NotEmpty`注解;
+
+```java
+/**
+ * 用户登录参数
+ * Created by macro on 2018/4/26.
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class UmsAdminLoginParam {
+    @NotEmpty
+    @ApiModelProperty(value = "用户名",required = true)
+    private String username;
+    @NotEmpty
+    @ApiModelProperty(value = "密码",required = true)
+    private String password;
+}
+```
+
+- 然后在登录接口中添加`@Validated`注解开启参数校验功能即可。
+
+```java
+/**
+ * 后台用户管理
+ * Created by macro on 2018/4/26.
+ */
+@Controller
+@Api(tags = "UmsAdminController", description = "后台用户管理")
+@RequestMapping("/admin")
+public class UmsAdminController {
+
+    @ApiOperation(value = "登录以后返回token")
+    @RequestMapping(value = "/login", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult login(@Validated @RequestBody UmsAdminLoginParam umsAdminLoginParam) {
+        String token = adminService.login(umsAdminLoginParam.getUsername(), umsAdminLoginParam.getPassword());
+        if (token == null) {
+            return CommonResult.validateFailed("用户名或密码错误");
+        }
+        Map<String, String> tokenMap = new HashMap<>();
+        tokenMap.put("token", token);
+        tokenMap.put("tokenHead", tokenHead);
+        return CommonResult.success(tokenMap);
+    }
+}
+```
+
+## 公众号
+
+学习不走弯路,关注公众号「**macrozheng**」,回复「**学习路线**」,获取mall项目专属学习路线!
+
+加微信群交流,公众号后台回复「**加群**」即可。
+
+![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg)
+
+## 许可证
+
+[Apache License 2.0](https://github.com/macrozheng/mall-tiny/blob/master/LICENSE)
+
+Copyright (c) 2018-2021 macrozheng

BIN
images/mall_tiny_start_01.png


BIN
images/mall_tiny_start_02.png


BIN
images/mall_tiny_start_03.png


BIN
images/mall_tiny_start_04.png


BIN
images/mall_tiny_start_05.png


BIN
images/mall_tiny_start_06.png


BIN
images/mall_tiny_start_07.png


BIN
images/mall_tiny_start_08.png


BIN
images/mall_tiny_start_09.png


+ 184 - 0
pom.xml

@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.macro.mall</groupId>
+    <artifactId>mall-tiny</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <name>mall-tiny</name>
+    <description>mall-tiny project</description>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <java.version>1.8</java.version>
+        <skipTests>true</skipTests>
+        <docker.host>http://192.168.3.101:2375</docker.host>
+        <docker.maven.plugin.version>1.2.2</docker.maven.plugin.version>
+        <druid.version>1.1.10</druid.version>
+        <hutool.version>4.5.7</hutool.version>
+        <swagger2.version>2.9.2</swagger2.version>
+        <swagger-models.version>1.6.0</swagger-models.version>
+        <swagger-annotations.version>1.6.0</swagger-annotations.version>
+        <mysql-connector.version>8.0.16</mysql-connector.version>
+        <jjwt.version>0.9.0</jjwt.version>
+        <mybatis-plus.version>3.3.2</mybatis-plus.version>
+        <velocity.version>2.2</velocity.version>
+    </properties>
+
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.3.0.RELEASE</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <dependencies>
+        <!--SpringBoot通用依赖模块-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!--SpringBoot校验框架-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <!--集成druid连接池-->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+            <version>${druid.version}</version>
+        </dependency>
+        <!--Mysql数据库驱动-->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>${mysql-connector.version}</version>
+        </dependency>
+        <!--Swagger-UI API文档生产工具-->
+        <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 2.9.2版本NumberFormatException-->
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-models</artifactId>
+            <version>${swagger-models.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>${swagger-annotations.version}</version>
+        </dependency>
+        <!--redis依赖配置-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <!--SpringSecurity依赖配置-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <!--Hutool Java工具包-->
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>${hutool.version}</version>
+        </dependency>
+        <!--JWT(Json Web Token)登录支持-->
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>${jjwt.version}</version>
+        </dependency>
+        <!--lombok依赖-->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <!--SpringBoot配置处理-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <!--MyBatis Plus 依赖-->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>${mybatis-plus.version}</version>
+        </dependency>
+        <!--MyBatis Plus 代码生成器-->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-generator</artifactId>
+            <version>${mybatis-plus.version}</version>
+        </dependency>
+        <!--Velocity模板引擎-->
+        <dependency>
+            <groupId>org.apache.velocity</groupId>
+            <artifactId>velocity-engine-core</artifactId>
+            <version>${velocity.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>com.spotify</groupId>
+                <artifactId>docker-maven-plugin</artifactId>
+                <version>${docker.maven.plugin.version}</version>
+                <executions>
+                    <execution>
+                        <id>build-image</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>build</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <imageName>mall-tiny/${project.artifactId}:${project.version}</imageName>
+                    <dockerHost>${docker.host}</dockerHost>
+                    <baseImage>java:8</baseImage>
+                    <entryPoint>["java", "-jar","/${project.build.finalName}.jar"]
+                    </entryPoint>
+                    <resources>
+                        <resource>
+                            <targetPath>/</targetPath>
+                            <directory>${project.build.directory}</directory>
+                            <include>${project.build.finalName}.jar</include>
+                        </resource>
+                    </resources>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 361 - 0
sql/mall_tiny.sql

@@ -0,0 +1,361 @@
+/*
+Navicat MySQL Data Transfer
+
+Source Server         : localhost
+Source Server Version : 50719
+Source Host           : localhost:3306
+Source Database       : mall_tiny
+
+Target Server Type    : MYSQL
+Target Server Version : 50719
+File Encoding         : 65001
+
+Date: 2020-08-24 14:06:42
+*/
+
+SET FOREIGN_KEY_CHECKS=0;
+
+-- ----------------------------
+-- Table structure for ums_admin
+-- ----------------------------
+DROP TABLE IF EXISTS `ums_admin`;
+CREATE TABLE `ums_admin` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `username` varchar(64) DEFAULT NULL,
+  `password` varchar(64) DEFAULT NULL,
+  `icon` varchar(500) DEFAULT NULL COMMENT '头像',
+  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
+  `nick_name` varchar(200) DEFAULT NULL COMMENT '昵称',
+  `note` varchar(500) DEFAULT NULL COMMENT '备注信息',
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  `login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
+  `status` int(1) DEFAULT '1' COMMENT '帐号启用状态:0->禁用;1->启用',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COMMENT='后台用户表';
+
+-- ----------------------------
+-- Records of ums_admin
+-- ----------------------------
+INSERT INTO `ums_admin` VALUES ('1', 'test', '$2a$10$NZ5o7r2E.ayT2ZoxgjlI.eJ6OEYqjH7INR/F.mXDbjZJi9HF0YCVG', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/timg.jpg', 'test@qq.com', '测试账号', null, '2018-09-29 13:55:30', '2018-09-29 13:55:39', '1');
+INSERT INTO `ums_admin` VALUES ('3', 'admin', '$2a$10$.E1FokumK5GIXWgKlg.Hc.i/0/2.qdAwYFL1zc5QHdyzpXOr38RZO', 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20180607/timg.jpg', 'admin@163.com', '系统管理员', '系统管理员', '2018-10-08 13:32:47', '2019-04-20 12:45:16', '1');
+INSERT INTO `ums_admin` VALUES ('4', 'macro', '$2a$10$Bx4jZPR7GhEpIQfefDQtVeS58GfT5n6mxs/b4nLLK65eMFa16topa', 'string', 'macro@qq.com', 'macro', 'macro专用', '2019-10-06 15:53:51', '2020-02-03 14:55:55', '1');
+INSERT INTO `ums_admin` VALUES ('6', 'productAdmin', '$2a$10$6/.J.p.6Bhn7ic4GfoB5D.pGd7xSiD1a9M6ht6yO0fxzlKJPjRAGm', null, 'product@qq.com', '商品管理员', '只有商品权限', '2020-02-07 16:15:08', null, '1');
+INSERT INTO `ums_admin` VALUES ('7', 'orderAdmin', '$2a$10$UqEhA9UZXjHHA3B.L9wNG.6aerrBjC6WHTtbv1FdvYPUI.7lkL6E.', null, 'order@qq.com', '订单管理员', '只有订单管理权限', '2020-02-07 16:15:50', null, '1');
+INSERT INTO `ums_admin` VALUES ('10', 'ceshi', '$2a$10$RaaNo9CC0RSms8mc/gJpCuOWndDT4pHH0u5XgZdAAYFs1Uq4sOPRi', null, 'ceshi@qq.com', 'ceshi', null, '2020-03-13 16:23:30', null, '1');
+
+-- ----------------------------
+-- Table structure for ums_admin_login_log
+-- ----------------------------
+DROP TABLE IF EXISTS `ums_admin_login_log`;
+CREATE TABLE `ums_admin_login_log` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `admin_id` bigint(20) DEFAULT NULL,
+  `create_time` datetime DEFAULT NULL,
+  `ip` varchar(64) DEFAULT NULL,
+  `address` varchar(100) DEFAULT NULL,
+  `user_agent` varchar(100) DEFAULT NULL COMMENT '浏览器登录类型',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=287 DEFAULT CHARSET=utf8 COMMENT='后台用户登录日志表';
+
+-- ----------------------------
+-- Records of ums_admin_login_log
+-- ----------------------------
+INSERT INTO `ums_admin_login_log` VALUES ('285', '3', '2020-08-24 14:05:21', '0:0:0:0:0:0:0:1', null, null);
+INSERT INTO `ums_admin_login_log` VALUES ('286', '10', '2020-08-24 14:05:39', '0:0:0:0:0:0:0:1', null, null);
+
+-- ----------------------------
+-- Table structure for ums_admin_role_relation
+-- ----------------------------
+DROP TABLE IF EXISTS `ums_admin_role_relation`;
+CREATE TABLE `ums_admin_role_relation` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `admin_id` bigint(20) DEFAULT NULL,
+  `role_id` bigint(20) DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=utf8 COMMENT='后台用户和角色关系表';
+
+-- ----------------------------
+-- Records of ums_admin_role_relation
+-- ----------------------------
+INSERT INTO `ums_admin_role_relation` VALUES ('26', '3', '5');
+INSERT INTO `ums_admin_role_relation` VALUES ('27', '6', '1');
+INSERT INTO `ums_admin_role_relation` VALUES ('28', '7', '2');
+INSERT INTO `ums_admin_role_relation` VALUES ('29', '1', '5');
+INSERT INTO `ums_admin_role_relation` VALUES ('30', '4', '5');
+INSERT INTO `ums_admin_role_relation` VALUES ('31', '8', '5');
+INSERT INTO `ums_admin_role_relation` VALUES ('34', '12', '6');
+INSERT INTO `ums_admin_role_relation` VALUES ('38', '13', '5');
+INSERT INTO `ums_admin_role_relation` VALUES ('39', '10', '8');
+
+-- ----------------------------
+-- Table structure for ums_menu
+-- ----------------------------
+DROP TABLE IF EXISTS `ums_menu`;
+CREATE TABLE `ums_menu` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `parent_id` bigint(20) DEFAULT NULL COMMENT '父级ID',
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  `title` varchar(100) DEFAULT NULL COMMENT '菜单名称',
+  `level` int(4) DEFAULT NULL COMMENT '菜单级数',
+  `sort` int(4) DEFAULT NULL COMMENT '菜单排序',
+  `name` varchar(100) DEFAULT NULL COMMENT '前端名称',
+  `icon` varchar(200) DEFAULT NULL COMMENT '前端图标',
+  `hidden` int(1) DEFAULT NULL COMMENT '前端隐藏',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8 COMMENT='后台菜单表';
+
+-- ----------------------------
+-- Records of ums_menu
+-- ----------------------------
+INSERT INTO `ums_menu` VALUES ('1', '0', '2020-02-02 14:50:36', '商品', '0', '0', 'pms', 'product', '1');
+INSERT INTO `ums_menu` VALUES ('2', '1', '2020-02-02 14:51:50', '商品列表', '1', '0', 'product', 'product-list', '0');
+INSERT INTO `ums_menu` VALUES ('3', '1', '2020-02-02 14:52:44', '添加商品', '1', '0', 'addProduct', 'product-add', '0');
+INSERT INTO `ums_menu` VALUES ('4', '1', '2020-02-02 14:53:51', '商品分类', '1', '0', 'productCate', 'product-cate', '0');
+INSERT INTO `ums_menu` VALUES ('5', '1', '2020-02-02 14:54:51', '商品类型', '1', '0', 'productAttr', 'product-attr', '0');
+INSERT INTO `ums_menu` VALUES ('6', '1', '2020-02-02 14:56:29', '品牌管理', '1', '0', 'brand', 'product-brand', '0');
+INSERT INTO `ums_menu` VALUES ('7', '0', '2020-02-02 16:54:07', '订单', '0', '0', 'oms', 'order', '1');
+INSERT INTO `ums_menu` VALUES ('8', '7', '2020-02-02 16:55:18', '订单列表', '1', '0', 'order', 'product-list', '0');
+INSERT INTO `ums_menu` VALUES ('9', '7', '2020-02-02 16:56:46', '订单设置', '1', '0', 'orderSetting', 'order-setting', '0');
+INSERT INTO `ums_menu` VALUES ('10', '7', '2020-02-02 16:57:39', '退货申请处理', '1', '0', 'returnApply', 'order-return', '0');
+INSERT INTO `ums_menu` VALUES ('11', '7', '2020-02-02 16:59:40', '退货原因设置', '1', '0', 'returnReason', 'order-return-reason', '0');
+INSERT INTO `ums_menu` VALUES ('12', '0', '2020-02-04 16:18:00', '营销', '0', '0', 'sms', 'sms', '1');
+INSERT INTO `ums_menu` VALUES ('13', '12', '2020-02-04 16:19:22', '秒杀活动列表', '1', '0', 'flash', 'sms-flash', '0');
+INSERT INTO `ums_menu` VALUES ('14', '12', '2020-02-04 16:20:16', '优惠券列表', '1', '0', 'coupon', 'sms-coupon', '0');
+INSERT INTO `ums_menu` VALUES ('16', '12', '2020-02-07 16:22:38', '品牌推荐', '1', '0', 'homeBrand', 'product-brand', '0');
+INSERT INTO `ums_menu` VALUES ('17', '12', '2020-02-07 16:23:14', '新品推荐', '1', '0', 'homeNew', 'sms-new', '0');
+INSERT INTO `ums_menu` VALUES ('18', '12', '2020-02-07 16:26:38', '人气推荐', '1', '0', 'homeHot', 'sms-hot', '0');
+INSERT INTO `ums_menu` VALUES ('19', '12', '2020-02-07 16:28:16', '专题推荐', '1', '0', 'homeSubject', 'sms-subject', '0');
+INSERT INTO `ums_menu` VALUES ('20', '12', '2020-02-07 16:28:42', '广告列表', '1', '0', 'homeAdvertise', 'sms-ad', '0');
+INSERT INTO `ums_menu` VALUES ('21', '0', '2020-02-07 16:29:13', '权限', '0', '0', 'ums', 'ums', '0');
+INSERT INTO `ums_menu` VALUES ('22', '21', '2020-02-07 16:29:51', '用户列表', '1', '0', 'admin', 'ums-admin', '0');
+INSERT INTO `ums_menu` VALUES ('23', '21', '2020-02-07 16:30:13', '角色列表', '1', '0', 'role', 'ums-role', '0');
+INSERT INTO `ums_menu` VALUES ('24', '21', '2020-02-07 16:30:53', '菜单列表', '1', '0', 'menu', 'ums-menu', '0');
+INSERT INTO `ums_menu` VALUES ('25', '21', '2020-02-07 16:31:13', '资源列表', '1', '0', 'resource', 'ums-resource', '0');
+
+-- ----------------------------
+-- Table structure for ums_resource
+-- ----------------------------
+DROP TABLE IF EXISTS `ums_resource`;
+CREATE TABLE `ums_resource` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  `name` varchar(200) DEFAULT NULL COMMENT '资源名称',
+  `url` varchar(200) DEFAULT NULL COMMENT '资源URL',
+  `description` varchar(500) DEFAULT NULL COMMENT '描述',
+  `category_id` bigint(20) DEFAULT NULL COMMENT '资源分类ID',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8 COMMENT='后台资源表';
+
+-- ----------------------------
+-- Records of ums_resource
+-- ----------------------------
+INSERT INTO `ums_resource` VALUES ('1', '2020-02-04 17:04:55', '商品品牌管理', '/brand/**', null, '1');
+INSERT INTO `ums_resource` VALUES ('2', '2020-02-04 17:05:35', '商品属性分类管理', '/productAttribute/**', null, '1');
+INSERT INTO `ums_resource` VALUES ('3', '2020-02-04 17:06:13', '商品属性管理', '/productAttribute/**', null, '1');
+INSERT INTO `ums_resource` VALUES ('4', '2020-02-04 17:07:15', '商品分类管理', '/productCategory/**', null, '1');
+INSERT INTO `ums_resource` VALUES ('5', '2020-02-04 17:09:16', '商品管理', '/product/**', null, '1');
+INSERT INTO `ums_resource` VALUES ('6', '2020-02-04 17:09:53', '商品库存管理', '/sku/**', null, '1');
+INSERT INTO `ums_resource` VALUES ('8', '2020-02-05 14:43:37', '订单管理', '/order/**', '', '2');
+INSERT INTO `ums_resource` VALUES ('9', '2020-02-05 14:44:22', ' 订单退货申请管理', '/returnApply/**', '', '2');
+INSERT INTO `ums_resource` VALUES ('10', '2020-02-05 14:45:08', '退货原因管理', '/returnReason/**', '', '2');
+INSERT INTO `ums_resource` VALUES ('11', '2020-02-05 14:45:43', '订单设置管理', '/orderSetting/**', '', '2');
+INSERT INTO `ums_resource` VALUES ('12', '2020-02-05 14:46:23', '收货地址管理', '/companyAddress/**', '', '2');
+INSERT INTO `ums_resource` VALUES ('13', '2020-02-07 16:37:22', '优惠券管理', '/coupon/**', '', '3');
+INSERT INTO `ums_resource` VALUES ('14', '2020-02-07 16:37:59', '优惠券领取记录管理', '/couponHistory/**', '', '3');
+INSERT INTO `ums_resource` VALUES ('15', '2020-02-07 16:38:28', '限时购活动管理', '/flash/**', '', '3');
+INSERT INTO `ums_resource` VALUES ('16', '2020-02-07 16:38:59', '限时购商品关系管理', '/flashProductRelation/**', '', '3');
+INSERT INTO `ums_resource` VALUES ('17', '2020-02-07 16:39:22', '限时购场次管理', '/flashSession/**', '', '3');
+INSERT INTO `ums_resource` VALUES ('18', '2020-02-07 16:40:07', '首页轮播广告管理', '/home/advertise/**', '', '3');
+INSERT INTO `ums_resource` VALUES ('19', '2020-02-07 16:40:34', '首页品牌管理', '/home/brand/**', '', '3');
+INSERT INTO `ums_resource` VALUES ('20', '2020-02-07 16:41:06', '首页新品管理', '/home/newProduct/**', '', '3');
+INSERT INTO `ums_resource` VALUES ('21', '2020-02-07 16:42:16', '首页人气推荐管理', '/home/recommendProduct/**', '', '3');
+INSERT INTO `ums_resource` VALUES ('22', '2020-02-07 16:42:48', '首页专题推荐管理', '/home/recommendSubject/**', '', '3');
+INSERT INTO `ums_resource` VALUES ('23', '2020-02-07 16:44:56', ' 商品优选管理', '/prefrenceArea/**', '', '5');
+INSERT INTO `ums_resource` VALUES ('24', '2020-02-07 16:45:39', '商品专题管理', '/subject/**', '', '5');
+INSERT INTO `ums_resource` VALUES ('25', '2020-02-07 16:47:34', '后台用户管理', '/admin/**', '', '4');
+INSERT INTO `ums_resource` VALUES ('26', '2020-02-07 16:48:24', '后台用户角色管理', '/role/**', '', '4');
+INSERT INTO `ums_resource` VALUES ('27', '2020-02-07 16:48:48', '后台菜单管理', '/menu/**', '', '4');
+INSERT INTO `ums_resource` VALUES ('28', '2020-02-07 16:49:18', '后台资源分类管理', '/resourceCategory/**', '', '4');
+INSERT INTO `ums_resource` VALUES ('29', '2020-02-07 16:49:45', '后台资源管理', '/resource/**', '', '4');
+
+-- ----------------------------
+-- Table structure for ums_resource_category
+-- ----------------------------
+DROP TABLE IF EXISTS `ums_resource_category`;
+CREATE TABLE `ums_resource_category` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  `name` varchar(200) DEFAULT NULL COMMENT '分类名称',
+  `sort` int(4) DEFAULT NULL COMMENT '排序',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='资源分类表';
+
+-- ----------------------------
+-- Records of ums_resource_category
+-- ----------------------------
+INSERT INTO `ums_resource_category` VALUES ('1', '2020-02-05 10:21:44', '商品模块', '0');
+INSERT INTO `ums_resource_category` VALUES ('2', '2020-02-05 10:22:34', '订单模块', '0');
+INSERT INTO `ums_resource_category` VALUES ('3', '2020-02-05 10:22:48', '营销模块', '0');
+INSERT INTO `ums_resource_category` VALUES ('4', '2020-02-05 10:23:04', '权限模块', '0');
+INSERT INTO `ums_resource_category` VALUES ('5', '2020-02-07 16:34:27', '内容模块', '0');
+INSERT INTO `ums_resource_category` VALUES ('6', '2020-02-07 16:35:49', '其他模块', '0');
+
+-- ----------------------------
+-- Table structure for ums_role
+-- ----------------------------
+DROP TABLE IF EXISTS `ums_role`;
+CREATE TABLE `ums_role` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `name` varchar(100) DEFAULT NULL COMMENT '名称',
+  `description` varchar(500) DEFAULT NULL COMMENT '描述',
+  `admin_count` int(11) DEFAULT NULL COMMENT '后台用户数量',
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  `status` int(1) DEFAULT '1' COMMENT '启用状态:0->禁用;1->启用',
+  `sort` int(11) DEFAULT '0',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='后台用户角色表';
+
+-- ----------------------------
+-- Records of ums_role
+-- ----------------------------
+INSERT INTO `ums_role` VALUES ('1', '商品管理员', '只能查看及操作商品', '0', '2020-02-03 16:50:37', '1', '0');
+INSERT INTO `ums_role` VALUES ('2', '订单管理员', '只能查看及操作订单', '0', '2018-09-30 15:53:45', '1', '0');
+INSERT INTO `ums_role` VALUES ('5', '超级管理员', '拥有所有查看和操作功能', '0', '2020-02-02 15:11:05', '1', '0');
+INSERT INTO `ums_role` VALUES ('8', '权限管理员', '用于权限模块所有操作功能', '0', '2020-08-24 10:57:35', '1', '0');
+
+-- ----------------------------
+-- Table structure for ums_role_menu_relation
+-- ----------------------------
+DROP TABLE IF EXISTS `ums_role_menu_relation`;
+CREATE TABLE `ums_role_menu_relation` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',
+  `menu_id` bigint(20) DEFAULT NULL COMMENT '菜单ID',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8 COMMENT='后台角色菜单关系表';
+
+-- ----------------------------
+-- Records of ums_role_menu_relation
+-- ----------------------------
+INSERT INTO `ums_role_menu_relation` VALUES ('33', '1', '1');
+INSERT INTO `ums_role_menu_relation` VALUES ('34', '1', '2');
+INSERT INTO `ums_role_menu_relation` VALUES ('35', '1', '3');
+INSERT INTO `ums_role_menu_relation` VALUES ('36', '1', '4');
+INSERT INTO `ums_role_menu_relation` VALUES ('37', '1', '5');
+INSERT INTO `ums_role_menu_relation` VALUES ('38', '1', '6');
+INSERT INTO `ums_role_menu_relation` VALUES ('53', '2', '7');
+INSERT INTO `ums_role_menu_relation` VALUES ('54', '2', '8');
+INSERT INTO `ums_role_menu_relation` VALUES ('55', '2', '9');
+INSERT INTO `ums_role_menu_relation` VALUES ('56', '2', '10');
+INSERT INTO `ums_role_menu_relation` VALUES ('57', '2', '11');
+INSERT INTO `ums_role_menu_relation` VALUES ('72', '5', '1');
+INSERT INTO `ums_role_menu_relation` VALUES ('73', '5', '2');
+INSERT INTO `ums_role_menu_relation` VALUES ('74', '5', '3');
+INSERT INTO `ums_role_menu_relation` VALUES ('75', '5', '4');
+INSERT INTO `ums_role_menu_relation` VALUES ('76', '5', '5');
+INSERT INTO `ums_role_menu_relation` VALUES ('77', '5', '6');
+INSERT INTO `ums_role_menu_relation` VALUES ('78', '5', '7');
+INSERT INTO `ums_role_menu_relation` VALUES ('79', '5', '8');
+INSERT INTO `ums_role_menu_relation` VALUES ('80', '5', '9');
+INSERT INTO `ums_role_menu_relation` VALUES ('81', '5', '10');
+INSERT INTO `ums_role_menu_relation` VALUES ('82', '5', '11');
+INSERT INTO `ums_role_menu_relation` VALUES ('83', '5', '12');
+INSERT INTO `ums_role_menu_relation` VALUES ('84', '5', '13');
+INSERT INTO `ums_role_menu_relation` VALUES ('85', '5', '14');
+INSERT INTO `ums_role_menu_relation` VALUES ('86', '5', '16');
+INSERT INTO `ums_role_menu_relation` VALUES ('87', '5', '17');
+INSERT INTO `ums_role_menu_relation` VALUES ('88', '5', '18');
+INSERT INTO `ums_role_menu_relation` VALUES ('89', '5', '19');
+INSERT INTO `ums_role_menu_relation` VALUES ('90', '5', '20');
+INSERT INTO `ums_role_menu_relation` VALUES ('91', '5', '21');
+INSERT INTO `ums_role_menu_relation` VALUES ('92', '5', '22');
+INSERT INTO `ums_role_menu_relation` VALUES ('93', '5', '23');
+INSERT INTO `ums_role_menu_relation` VALUES ('94', '5', '24');
+INSERT INTO `ums_role_menu_relation` VALUES ('95', '5', '25');
+INSERT INTO `ums_role_menu_relation` VALUES ('96', '6', '21');
+INSERT INTO `ums_role_menu_relation` VALUES ('97', '6', '22');
+INSERT INTO `ums_role_menu_relation` VALUES ('98', '6', '23');
+INSERT INTO `ums_role_menu_relation` VALUES ('99', '6', '24');
+INSERT INTO `ums_role_menu_relation` VALUES ('100', '6', '25');
+INSERT INTO `ums_role_menu_relation` VALUES ('101', '7', '21');
+INSERT INTO `ums_role_menu_relation` VALUES ('102', '7', '22');
+INSERT INTO `ums_role_menu_relation` VALUES ('103', '7', '23');
+INSERT INTO `ums_role_menu_relation` VALUES ('104', '7', '24');
+INSERT INTO `ums_role_menu_relation` VALUES ('105', '7', '25');
+INSERT INTO `ums_role_menu_relation` VALUES ('106', '8', '21');
+INSERT INTO `ums_role_menu_relation` VALUES ('107', '8', '22');
+INSERT INTO `ums_role_menu_relation` VALUES ('108', '8', '23');
+INSERT INTO `ums_role_menu_relation` VALUES ('109', '8', '24');
+INSERT INTO `ums_role_menu_relation` VALUES ('110', '8', '25');
+
+-- ----------------------------
+-- Table structure for ums_role_resource_relation
+-- ----------------------------
+DROP TABLE IF EXISTS `ums_role_resource_relation`;
+CREATE TABLE `ums_role_resource_relation` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `role_id` bigint(20) DEFAULT NULL COMMENT '角色ID',
+  `resource_id` bigint(20) DEFAULT NULL COMMENT '资源ID',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=216 DEFAULT CHARSET=utf8 COMMENT='后台角色资源关系表';
+
+-- ----------------------------
+-- Records of ums_role_resource_relation
+-- ----------------------------
+INSERT INTO `ums_role_resource_relation` VALUES ('103', '2', '8');
+INSERT INTO `ums_role_resource_relation` VALUES ('104', '2', '9');
+INSERT INTO `ums_role_resource_relation` VALUES ('105', '2', '10');
+INSERT INTO `ums_role_resource_relation` VALUES ('106', '2', '11');
+INSERT INTO `ums_role_resource_relation` VALUES ('107', '2', '12');
+INSERT INTO `ums_role_resource_relation` VALUES ('142', '5', '1');
+INSERT INTO `ums_role_resource_relation` VALUES ('143', '5', '2');
+INSERT INTO `ums_role_resource_relation` VALUES ('144', '5', '3');
+INSERT INTO `ums_role_resource_relation` VALUES ('145', '5', '4');
+INSERT INTO `ums_role_resource_relation` VALUES ('146', '5', '5');
+INSERT INTO `ums_role_resource_relation` VALUES ('147', '5', '6');
+INSERT INTO `ums_role_resource_relation` VALUES ('148', '5', '8');
+INSERT INTO `ums_role_resource_relation` VALUES ('149', '5', '9');
+INSERT INTO `ums_role_resource_relation` VALUES ('150', '5', '10');
+INSERT INTO `ums_role_resource_relation` VALUES ('151', '5', '11');
+INSERT INTO `ums_role_resource_relation` VALUES ('152', '5', '12');
+INSERT INTO `ums_role_resource_relation` VALUES ('153', '5', '13');
+INSERT INTO `ums_role_resource_relation` VALUES ('154', '5', '14');
+INSERT INTO `ums_role_resource_relation` VALUES ('155', '5', '15');
+INSERT INTO `ums_role_resource_relation` VALUES ('156', '5', '16');
+INSERT INTO `ums_role_resource_relation` VALUES ('157', '5', '17');
+INSERT INTO `ums_role_resource_relation` VALUES ('158', '5', '18');
+INSERT INTO `ums_role_resource_relation` VALUES ('159', '5', '19');
+INSERT INTO `ums_role_resource_relation` VALUES ('160', '5', '20');
+INSERT INTO `ums_role_resource_relation` VALUES ('161', '5', '21');
+INSERT INTO `ums_role_resource_relation` VALUES ('162', '5', '22');
+INSERT INTO `ums_role_resource_relation` VALUES ('163', '5', '23');
+INSERT INTO `ums_role_resource_relation` VALUES ('164', '5', '24');
+INSERT INTO `ums_role_resource_relation` VALUES ('165', '5', '25');
+INSERT INTO `ums_role_resource_relation` VALUES ('166', '5', '26');
+INSERT INTO `ums_role_resource_relation` VALUES ('167', '5', '27');
+INSERT INTO `ums_role_resource_relation` VALUES ('168', '5', '28');
+INSERT INTO `ums_role_resource_relation` VALUES ('169', '5', '29');
+INSERT INTO `ums_role_resource_relation` VALUES ('170', '1', '1');
+INSERT INTO `ums_role_resource_relation` VALUES ('171', '1', '2');
+INSERT INTO `ums_role_resource_relation` VALUES ('172', '1', '3');
+INSERT INTO `ums_role_resource_relation` VALUES ('173', '1', '4');
+INSERT INTO `ums_role_resource_relation` VALUES ('174', '1', '5');
+INSERT INTO `ums_role_resource_relation` VALUES ('175', '1', '6');
+INSERT INTO `ums_role_resource_relation` VALUES ('176', '1', '23');
+INSERT INTO `ums_role_resource_relation` VALUES ('177', '1', '24');
+INSERT INTO `ums_role_resource_relation` VALUES ('178', '6', '25');
+INSERT INTO `ums_role_resource_relation` VALUES ('179', '6', '26');
+INSERT INTO `ums_role_resource_relation` VALUES ('180', '6', '27');
+INSERT INTO `ums_role_resource_relation` VALUES ('181', '6', '28');
+INSERT INTO `ums_role_resource_relation` VALUES ('182', '6', '29');
+INSERT INTO `ums_role_resource_relation` VALUES ('205', '7', '25');
+INSERT INTO `ums_role_resource_relation` VALUES ('206', '7', '26');
+INSERT INTO `ums_role_resource_relation` VALUES ('207', '7', '27');
+INSERT INTO `ums_role_resource_relation` VALUES ('208', '7', '28');
+INSERT INTO `ums_role_resource_relation` VALUES ('209', '7', '29');
+INSERT INTO `ums_role_resource_relation` VALUES ('210', '7', '31');
+INSERT INTO `ums_role_resource_relation` VALUES ('211', '8', '25');
+INSERT INTO `ums_role_resource_relation` VALUES ('212', '8', '26');
+INSERT INTO `ums_role_resource_relation` VALUES ('213', '8', '27');
+INSERT INTO `ums_role_resource_relation` VALUES ('214', '8', '28');
+INSERT INTO `ums_role_resource_relation` VALUES ('215', '8', '29');

+ 13 - 0
src/main/java/com/macro/mall/tiny/MallTinyApplication.java

@@ -0,0 +1,13 @@
+package com.macro.mall.tiny;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class MallTinyApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(MallTinyApplication.class, args);
+    }
+
+}

+ 71 - 0
src/main/java/com/macro/mall/tiny/common/api/CommonPage.java

@@ -0,0 +1,71 @@
+package com.macro.mall.tiny.common.api;
+
+import cn.hutool.core.convert.Convert;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+
+import java.util.List;
+
+/**
+ * 分页数据封装类
+ * Created by macro on 2019/4/19.
+ */
+public class CommonPage<T> {
+    private Integer pageNum;
+    private Integer pageSize;
+    private Integer totalPage;
+    private Long total;
+    private List<T> list;
+
+    /**
+     * 将MyBatis Plus 分页结果转化为通用结果
+     */
+    public static <T> CommonPage<T> restPage(Page<T> pageResult) {
+        CommonPage<T> result = new CommonPage<>();
+        result.setPageNum(Convert.toInt(pageResult.getCurrent()));
+        result.setPageSize(Convert.toInt(pageResult.getSize()));
+        result.setTotal(pageResult.getTotal());
+        result.setTotalPage(Convert.toInt(pageResult.getTotal()/pageResult.getSize()+1));
+        result.setList(pageResult.getRecords());
+        return result;
+    }
+
+    public Integer getPageNum() {
+        return pageNum;
+    }
+
+    public void setPageNum(Integer pageNum) {
+        this.pageNum = pageNum;
+    }
+
+    public Integer getPageSize() {
+        return pageSize;
+    }
+
+    public void setPageSize(Integer pageSize) {
+        this.pageSize = pageSize;
+    }
+
+    public Integer getTotalPage() {
+        return totalPage;
+    }
+
+    public void setTotalPage(Integer totalPage) {
+        this.totalPage = totalPage;
+    }
+
+    public List<T> getList() {
+        return list;
+    }
+
+    public void setList(List<T> list) {
+        this.list = list;
+    }
+
+    public Long getTotal() {
+        return total;
+    }
+
+    public void setTotal(Long total) {
+        this.total = total;
+    }
+}

+ 124 - 0
src/main/java/com/macro/mall/tiny/common/api/CommonResult.java

@@ -0,0 +1,124 @@
+package com.macro.mall.tiny.common.api;
+
+/**
+ * 通用返回对象
+ * Created by macro on 2019/4/19.
+ */
+public class CommonResult<T> {
+    private long code;
+    private String message;
+    private T data;
+
+    protected CommonResult() {
+    }
+
+    protected CommonResult(long code, String message, T data) {
+        this.code = code;
+        this.message = message;
+        this.data = data;
+    }
+
+    /**
+     * 成功返回结果
+     *
+     * @param data 获取的数据
+     */
+    public static <T> CommonResult<T> success(T data) {
+        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
+    }
+
+    /**
+     * 成功返回结果
+     *
+     * @param data 获取的数据
+     * @param  message 提示信息
+     */
+    public static <T> CommonResult<T> success(T data, String message) {
+        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);
+    }
+
+    /**
+     * 失败返回结果
+     * @param errorCode 错误码
+     */
+    public static <T> CommonResult<T> failed(IErrorCode errorCode) {
+        return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);
+    }
+
+    /**
+     * 失败返回结果
+     * @param errorCode 错误码
+     * @param message 错误信息
+     */
+    public static <T> CommonResult<T> failed(IErrorCode errorCode,String message) {
+        return new CommonResult<T>(errorCode.getCode(), message, null);
+    }
+
+    /**
+     * 失败返回结果
+     * @param message 提示信息
+     */
+    public static <T> CommonResult<T> failed(String message) {
+        return new CommonResult<T>(ResultCode.FAILED.getCode(), message, null);
+    }
+
+    /**
+     * 失败返回结果
+     */
+    public static <T> CommonResult<T> failed() {
+        return failed(ResultCode.FAILED);
+    }
+
+    /**
+     * 参数验证失败返回结果
+     */
+    public static <T> CommonResult<T> validateFailed() {
+        return failed(ResultCode.VALIDATE_FAILED);
+    }
+
+    /**
+     * 参数验证失败返回结果
+     * @param message 提示信息
+     */
+    public static <T> CommonResult<T> validateFailed(String message) {
+        return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null);
+    }
+
+    /**
+     * 未登录返回结果
+     */
+    public static <T> CommonResult<T> unauthorized(T data) {
+        return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);
+    }
+
+    /**
+     * 未授权返回结果
+     */
+    public static <T> CommonResult<T> forbidden(T data) {
+        return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);
+    }
+
+    public long getCode() {
+        return code;
+    }
+
+    public void setCode(long code) {
+        this.code = code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public void setData(T data) {
+        this.data = data;
+    }
+}

+ 11 - 0
src/main/java/com/macro/mall/tiny/common/api/IErrorCode.java

@@ -0,0 +1,11 @@
+package com.macro.mall.tiny.common.api;
+
+/**
+ * 封装API的错误码
+ * Created by macro on 2019/4/19.
+ */
+public interface IErrorCode {
+    long getCode();
+
+    String getMessage();
+}

+ 28 - 0
src/main/java/com/macro/mall/tiny/common/api/ResultCode.java

@@ -0,0 +1,28 @@
+package com.macro.mall.tiny.common.api;
+
+/**
+ * 枚举了一些常用API操作码
+ * Created by macro on 2019/4/19.
+ */
+public enum ResultCode implements IErrorCode {
+    SUCCESS(200, "操作成功"),
+    FAILED(500, "操作失败"),
+    VALIDATE_FAILED(404, "参数检验失败"),
+    UNAUTHORIZED(401, "暂未登录或token已经过期"),
+    FORBIDDEN(403, "没有相关权限");
+    private long code;
+    private String message;
+
+    private ResultCode(long code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public long getCode() {
+        return code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+}

+ 68 - 0
src/main/java/com/macro/mall/tiny/common/config/BaseRedisConfig.java

@@ -0,0 +1,68 @@
+package com.macro.mall.tiny.common.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import com.macro.mall.tiny.common.service.RedisService;
+import com.macro.mall.tiny.common.service.impl.RedisServiceImpl;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.cache.RedisCacheWriter;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import java.time.Duration;
+
+/**
+ * Redis基础配置
+ * Created by macro on 2020/6/19.
+ */
+public class BaseRedisConfig {
+
+    @Bean
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+        RedisSerializer<Object> serializer = redisSerializer();
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setConnectionFactory(redisConnectionFactory);
+        redisTemplate.setKeySerializer(new StringRedisSerializer());
+        redisTemplate.setValueSerializer(serializer);
+        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+        redisTemplate.setHashValueSerializer(serializer);
+        redisTemplate.afterPropertiesSet();
+        return redisTemplate;
+    }
+
+    @Bean
+    public RedisSerializer<Object> redisSerializer() {
+        //创建JSON序列化器
+        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        //必须设置,否则无法将JSON转化为对象,会转化成Map类型
+        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
+        serializer.setObjectMapper(objectMapper);
+        return serializer;
+    }
+
+    @Bean
+    public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
+        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
+        //设置Redis缓存有效期为1天
+        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
+                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1));
+        return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
+    }
+
+
+    @Bean
+    public RedisService redisService(){
+        return new RedisServiceImpl();
+    }
+
+}

+ 81 - 0
src/main/java/com/macro/mall/tiny/common/config/BaseSwaggerConfig.java

@@ -0,0 +1,81 @@
+package com.macro.mall.tiny.common.config;
+
+import com.macro.mall.tiny.common.domain.SwaggerProperties;
+import org.springframework.context.annotation.Bean;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.*;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Swagger基础配置
+ * Created by macro on 2020/7/16.
+ */
+public abstract class BaseSwaggerConfig {
+
+    @Bean
+    public Docket createRestApi() {
+        SwaggerProperties swaggerProperties = swaggerProperties();
+        Docket docket = new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo(swaggerProperties))
+                .select()
+                .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getApiBasePackage()))
+                .paths(PathSelectors.any())
+                .build();
+        if (swaggerProperties.isEnableSecurity()) {
+            docket.securitySchemes(securitySchemes()).securityContexts(securityContexts());
+        }
+        return docket;
+    }
+
+    private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {
+        return new ApiInfoBuilder()
+                .title(swaggerProperties.getTitle())
+                .description(swaggerProperties.getDescription())
+                .contact(new Contact(swaggerProperties.getContactName(), swaggerProperties.getContactUrl(), swaggerProperties.getContactEmail()))
+                .version(swaggerProperties.getVersion())
+                .build();
+    }
+
+    private List<ApiKey> securitySchemes() {
+        //设置请求头信息
+        List<ApiKey> result = new ArrayList<>();
+        ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
+        result.add(apiKey);
+        return result;
+    }
+
+    private List<SecurityContext> securityContexts() {
+        //设置需要登录认证的路径
+        List<SecurityContext> result = new ArrayList<>();
+        result.add(getContextByPath("/*/.*"));
+        return result;
+    }
+
+    private SecurityContext getContextByPath(String pathRegex) {
+        return SecurityContext.builder()
+                .securityReferences(defaultAuth())
+                .forPaths(PathSelectors.regex(pathRegex))
+                .build();
+    }
+
+    private List<SecurityReference> defaultAuth() {
+        List<SecurityReference> result = new ArrayList<>();
+        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+        authorizationScopes[0] = authorizationScope;
+        result.add(new SecurityReference("Authorization", authorizationScopes));
+        return result;
+    }
+
+    /**
+     * 自定义Swagger配置
+     */
+    public abstract SwaggerProperties swaggerProperties();
+}

+ 47 - 0
src/main/java/com/macro/mall/tiny/common/domain/SwaggerProperties.java

@@ -0,0 +1,47 @@
+package com.macro.mall.tiny.common.domain;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * Swagger自定义配置
+ * Created by macro on 2020/7/16.
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Builder
+public class SwaggerProperties {
+    /**
+     * API文档生成基础路径
+     */
+    private String apiBasePackage;
+    /**
+     * 是否要启用登录认证
+     */
+    private boolean enableSecurity;
+    /**
+     * 文档标题
+     */
+    private String title;
+    /**
+     * 文档描述
+     */
+    private String description;
+    /**
+     * 文档版本
+     */
+    private String version;
+    /**
+     * 文档联系人姓名
+     */
+    private String contactName;
+    /**
+     * 文档联系人网址
+     */
+    private String contactUrl;
+    /**
+     * 文档联系人邮箱
+     */
+    private String contactEmail;
+}

+ 33 - 0
src/main/java/com/macro/mall/tiny/common/exception/ApiException.java

@@ -0,0 +1,33 @@
+package com.macro.mall.tiny.common.exception;
+
+
+import com.macro.mall.tiny.common.api.IErrorCode;
+
+/**
+ * 自定义API异常
+ * Created by macro on 2020/2/27.
+ */
+public class ApiException extends RuntimeException {
+    private IErrorCode errorCode;
+
+    public ApiException(IErrorCode errorCode) {
+        super(errorCode.getMessage());
+        this.errorCode = errorCode;
+    }
+
+    public ApiException(String message) {
+        super(message);
+    }
+
+    public ApiException(Throwable cause) {
+        super(cause);
+    }
+
+    public ApiException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public IErrorCode getErrorCode() {
+        return errorCode;
+    }
+}

+ 18 - 0
src/main/java/com/macro/mall/tiny/common/exception/Asserts.java

@@ -0,0 +1,18 @@
+package com.macro.mall.tiny.common.exception;
+
+
+import com.macro.mall.tiny.common.api.IErrorCode;
+
+/**
+ * 断言处理类,用于抛出各种API异常
+ * Created by macro on 2020/2/27.
+ */
+public class Asserts {
+    public static void fail(String message) {
+        throw new ApiException(message);
+    }
+
+    public static void fail(IErrorCode errorCode) {
+        throw new ApiException(errorCode);
+    }
+}

+ 55 - 0
src/main/java/com/macro/mall/tiny/common/exception/GlobalExceptionHandler.java

@@ -0,0 +1,55 @@
+package com.macro.mall.tiny.common.exception;
+
+import com.macro.mall.tiny.common.api.CommonResult;
+import org.springframework.validation.BindException;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+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.ResponseBody;
+
+/**
+ * 全局异常处理
+ * Created by macro on 2020/2/27.
+ */
+@ControllerAdvice
+public class GlobalExceptionHandler {
+
+    @ResponseBody
+    @ExceptionHandler(value = ApiException.class)
+    public CommonResult handle(ApiException e) {
+        if (e.getErrorCode() != null) {
+            return CommonResult.failed(e.getErrorCode());
+        }
+        return CommonResult.failed(e.getMessage());
+    }
+
+    @ResponseBody
+    @ExceptionHandler(value = MethodArgumentNotValidException.class)
+    public CommonResult handleValidException(MethodArgumentNotValidException e) {
+        BindingResult bindingResult = e.getBindingResult();
+        String message = null;
+        if (bindingResult.hasErrors()) {
+            FieldError fieldError = bindingResult.getFieldError();
+            if (fieldError != null) {
+                message = fieldError.getField()+fieldError.getDefaultMessage();
+            }
+        }
+        return CommonResult.validateFailed(message);
+    }
+
+    @ResponseBody
+    @ExceptionHandler(value = BindException.class)
+    public CommonResult handleValidException(BindException e) {
+        BindingResult bindingResult = e.getBindingResult();
+        String message = null;
+        if (bindingResult.hasErrors()) {
+            FieldError fieldError = bindingResult.getFieldError();
+            if (fieldError != null) {
+                message = fieldError.getField()+fieldError.getDefaultMessage();
+            }
+        }
+        return CommonResult.validateFailed(message);
+    }
+}

+ 182 - 0
src/main/java/com/macro/mall/tiny/common/service/RedisService.java

@@ -0,0 +1,182 @@
+package com.macro.mall.tiny.common.service;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * redis操作Service
+ * Created by macro on 2020/3/3.
+ */
+public interface RedisService {
+
+    /**
+     * 保存属性
+     */
+    void set(String key, Object value, long time);
+
+    /**
+     * 保存属性
+     */
+    void set(String key, Object value);
+
+    /**
+     * 获取属性
+     */
+    Object get(String key);
+
+    /**
+     * 删除属性
+     */
+    Boolean del(String key);
+
+    /**
+     * 批量删除属性
+     */
+    Long del(List<String> keys);
+
+    /**
+     * 设置过期时间
+     */
+    Boolean expire(String key, long time);
+
+    /**
+     * 获取过期时间
+     */
+    Long getExpire(String key);
+
+    /**
+     * 判断是否有该属性
+     */
+    Boolean hasKey(String key);
+
+    /**
+     * 按delta递增
+     */
+    Long incr(String key, long delta);
+
+    /**
+     * 按delta递减
+     */
+    Long decr(String key, long delta);
+
+    /**
+     * 获取Hash结构中的属性
+     */
+    Object hGet(String key, String hashKey);
+
+    /**
+     * 向Hash结构中放入一个属性
+     */
+    Boolean hSet(String key, String hashKey, Object value, long time);
+
+    /**
+     * 向Hash结构中放入一个属性
+     */
+    void hSet(String key, String hashKey, Object value);
+
+    /**
+     * 直接获取整个Hash结构
+     */
+    Map<Object, Object> hGetAll(String key);
+
+    /**
+     * 直接设置整个Hash结构
+     */
+    Boolean hSetAll(String key, Map<String, Object> map, long time);
+
+    /**
+     * 直接设置整个Hash结构
+     */
+    void hSetAll(String key, Map<String, ?> map);
+
+    /**
+     * 删除Hash结构中的属性
+     */
+    void hDel(String key, Object... hashKey);
+
+    /**
+     * 判断Hash结构中是否有该属性
+     */
+    Boolean hHasKey(String key, String hashKey);
+
+    /**
+     * Hash结构中属性递增
+     */
+    Long hIncr(String key, String hashKey, Long delta);
+
+    /**
+     * Hash结构中属性递减
+     */
+    Long hDecr(String key, String hashKey, Long delta);
+
+    /**
+     * 获取Set结构
+     */
+    Set<Object> sMembers(String key);
+
+    /**
+     * 向Set结构中添加属性
+     */
+    Long sAdd(String key, Object... values);
+
+    /**
+     * 向Set结构中添加属性
+     */
+    Long sAdd(String key, long time, Object... values);
+
+    /**
+     * 是否为Set中的属性
+     */
+    Boolean sIsMember(String key, Object value);
+
+    /**
+     * 获取Set结构的长度
+     */
+    Long sSize(String key);
+
+    /**
+     * 删除Set结构中的属性
+     */
+    Long sRemove(String key, Object... values);
+
+    /**
+     * 获取List结构中的属性
+     */
+    List<Object> lRange(String key, long start, long end);
+
+    /**
+     * 获取List结构的长度
+     */
+    Long lSize(String key);
+
+    /**
+     * 根据索引获取List中的属性
+     */
+    Object lIndex(String key, long index);
+
+    /**
+     * 向List结构中添加属性
+     */
+    Long lPush(String key, Object value);
+
+    /**
+     * 向List结构中添加属性
+     */
+    Long lPush(String key, Object value, long time);
+
+    /**
+     * 向List结构中批量添加属性
+     */
+    Long lPushAll(String key, Object... values);
+
+    /**
+     * 向List结构中批量添加属性
+     */
+    Long lPushAll(String key, Long time, Object... values);
+
+    /**
+     * 从List结构中移除属性
+     */
+    Long lRemove(String key, long count, Object value);
+}

+ 197 - 0
src/main/java/com/macro/mall/tiny/common/service/impl/RedisServiceImpl.java

@@ -0,0 +1,197 @@
+package com.macro.mall.tiny.common.service.impl;
+
+import com.macro.mall.tiny.common.service.RedisService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * redis操作实现类
+ * Created by macro on 2020/3/3.
+ */
+public class RedisServiceImpl implements RedisService {
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+
+    @Override
+    public void set(String key, Object value, long time) {
+        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public void set(String key, Object value) {
+        redisTemplate.opsForValue().set(key, value);
+    }
+
+    @Override
+    public Object get(String key) {
+        return redisTemplate.opsForValue().get(key);
+    }
+
+    @Override
+    public Boolean del(String key) {
+        return redisTemplate.delete(key);
+    }
+
+    @Override
+    public Long del(List<String> keys) {
+        return redisTemplate.delete(keys);
+    }
+
+    @Override
+    public Boolean expire(String key, long time) {
+        return redisTemplate.expire(key, time, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public Long getExpire(String key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public Boolean hasKey(String key) {
+        return redisTemplate.hasKey(key);
+    }
+
+    @Override
+    public Long incr(String key, long delta) {
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+
+    @Override
+    public Long decr(String key, long delta) {
+        return redisTemplate.opsForValue().increment(key, -delta);
+    }
+
+    @Override
+    public Object hGet(String key, String hashKey) {
+        return redisTemplate.opsForHash().get(key, hashKey);
+    }
+
+    @Override
+    public Boolean hSet(String key, String hashKey, Object value, long time) {
+        redisTemplate.opsForHash().put(key, hashKey, value);
+        return expire(key, time);
+    }
+
+    @Override
+    public void hSet(String key, String hashKey, Object value) {
+        redisTemplate.opsForHash().put(key, hashKey, value);
+    }
+
+    @Override
+    public Map<Object, Object> hGetAll(String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    @Override
+    public Boolean hSetAll(String key, Map<String, Object> map, long time) {
+        redisTemplate.opsForHash().putAll(key, map);
+        return expire(key, time);
+    }
+
+    @Override
+    public void hSetAll(String key, Map<String, ?> map) {
+        redisTemplate.opsForHash().putAll(key, map);
+    }
+
+    @Override
+    public void hDel(String key, Object... hashKey) {
+        redisTemplate.opsForHash().delete(key, hashKey);
+    }
+
+    @Override
+    public Boolean hHasKey(String key, String hashKey) {
+        return redisTemplate.opsForHash().hasKey(key, hashKey);
+    }
+
+    @Override
+    public Long hIncr(String key, String hashKey, Long delta) {
+        return redisTemplate.opsForHash().increment(key, hashKey, delta);
+    }
+
+    @Override
+    public Long hDecr(String key, String hashKey, Long delta) {
+        return redisTemplate.opsForHash().increment(key, hashKey, -delta);
+    }
+
+    @Override
+    public Set<Object> sMembers(String key) {
+        return redisTemplate.opsForSet().members(key);
+    }
+
+    @Override
+    public Long sAdd(String key, Object... values) {
+        return redisTemplate.opsForSet().add(key, values);
+    }
+
+    @Override
+    public Long sAdd(String key, long time, Object... values) {
+        Long count = redisTemplate.opsForSet().add(key, values);
+        expire(key, time);
+        return count;
+    }
+
+    @Override
+    public Boolean sIsMember(String key, Object value) {
+        return redisTemplate.opsForSet().isMember(key, value);
+    }
+
+    @Override
+    public Long sSize(String key) {
+        return redisTemplate.opsForSet().size(key);
+    }
+
+    @Override
+    public Long sRemove(String key, Object... values) {
+        return redisTemplate.opsForSet().remove(key, values);
+    }
+
+    @Override
+    public List<Object> lRange(String key, long start, long end) {
+        return redisTemplate.opsForList().range(key, start, end);
+    }
+
+    @Override
+    public Long lSize(String key) {
+        return redisTemplate.opsForList().size(key);
+    }
+
+    @Override
+    public Object lIndex(String key, long index) {
+        return redisTemplate.opsForList().index(key, index);
+    }
+
+    @Override
+    public Long lPush(String key, Object value) {
+        return redisTemplate.opsForList().rightPush(key, value);
+    }
+
+    @Override
+    public Long lPush(String key, Object value, long time) {
+        Long index = redisTemplate.opsForList().rightPush(key, value);
+        expire(key, time);
+        return index;
+    }
+
+    @Override
+    public Long lPushAll(String key, Object... values) {
+        return redisTemplate.opsForList().rightPushAll(key, values);
+    }
+
+    @Override
+    public Long lPushAll(String key, Long time, Object... values) {
+        Long count = redisTemplate.opsForList().rightPushAll(key, values);
+        expire(key, time);
+        return count;
+    }
+
+    @Override
+    public Long lRemove(String key, long count, Object value) {
+        return redisTemplate.opsForList().remove(key, count, value);
+    }
+}

+ 34 - 0
src/main/java/com/macro/mall/tiny/config/GlobalCorsConfig.java

@@ -0,0 +1,34 @@
+package com.macro.mall.tiny.config;
+
+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;
+
+/**
+ * 全局跨域配置
+ * Created by macro on 2019/7/27.
+ */
+@Configuration
+public class GlobalCorsConfig {
+
+    /**
+     * 允许跨域调用的过滤器
+     */
+    @Bean
+    public CorsFilter corsFilter() {
+        CorsConfiguration config = new CorsConfiguration();
+        //允许所有域名进行跨域调用
+        config.addAllowedOrigin("*");
+        //允许跨越发送cookie
+        config.setAllowCredentials(true);
+        //放行全部原始头信息
+        config.addAllowedHeader("*");
+        //允许所有请求方法跨域调用
+        config.addAllowedMethod("*");
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/**", config);
+        return new CorsFilter(source);
+    }
+}

+ 54 - 0
src/main/java/com/macro/mall/tiny/config/MallSecurityConfig.java

@@ -0,0 +1,54 @@
+package com.macro.mall.tiny.config;
+
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+import com.macro.mall.tiny.modules.ums.service.UmsAdminService;
+import com.macro.mall.tiny.modules.ums.service.UmsResourceService;
+import com.macro.mall.tiny.security.component.DynamicSecurityService;
+import com.macro.mall.tiny.security.config.SecurityConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.core.userdetails.UserDetailsService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * mall-security模块相关配置
+ * Created by macro on 2019/11/9.
+ */
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class MallSecurityConfig extends SecurityConfig {
+
+    @Autowired
+    private UmsAdminService adminService;
+    @Autowired
+    private UmsResourceService resourceService;
+
+    @Bean
+    public UserDetailsService userDetailsService() {
+        //获取登录用户信息
+        return username -> adminService.loadUserByUsername(username);
+    }
+
+    @Bean
+    public DynamicSecurityService dynamicSecurityService() {
+        return new DynamicSecurityService() {
+            @Override
+            public Map<String, ConfigAttribute> loadDataSource() {
+                Map<String, ConfigAttribute> map = new ConcurrentHashMap<>();
+                List<UmsResource> resourceList = resourceService.list();
+                for (UmsResource resource : resourceList) {
+                    map.put(resource.getUrl(), new org.springframework.security.access.SecurityConfig(resource.getId() + ":" + resource.getName()));
+                }
+                return map;
+            }
+        };
+    }
+}

+ 25 - 0
src/main/java/com/macro/mall/tiny/config/MyBatisConfig.java

@@ -0,0 +1,25 @@
+package com.macro.mall.tiny.config;
+
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+/**
+ * MyBatis配置类
+ * Created by macro on 2019/4/8.
+ */
+@Configuration
+@EnableTransactionManagement
+@MapperScan({"com.macro.mall.tiny.modules.*.mapper",
+        "com.macro.mall.tiny.modules.*.mapper"})
+public class MyBatisConfig {
+    @Bean
+    public PaginationInterceptor paginationInterceptor() {
+        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
+        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
+        return paginationInterceptor;
+    }
+}

+ 15 - 0
src/main/java/com/macro/mall/tiny/config/RedisConfig.java

@@ -0,0 +1,15 @@
+package com.macro.mall.tiny.config;
+
+import com.macro.mall.tiny.common.config.BaseRedisConfig;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Redis配置类
+ * Created by macro on 2020/3/2.
+ */
+@EnableCaching
+@Configuration
+public class RedisConfig extends BaseRedisConfig {
+
+}

+ 27 - 0
src/main/java/com/macro/mall/tiny/config/SwaggerConfig.java

@@ -0,0 +1,27 @@
+package com.macro.mall.tiny.config;
+
+import com.macro.mall.tiny.common.config.BaseSwaggerConfig;
+import com.macro.mall.tiny.common.domain.SwaggerProperties;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * Swagger API文档相关配置
+ * Created by macro on 2018/4/26.
+ */
+@Configuration
+@EnableSwagger2
+public class SwaggerConfig extends BaseSwaggerConfig {
+
+    @Override
+    public SwaggerProperties swaggerProperties() {
+        return SwaggerProperties.builder()
+                .apiBasePackage("com.macro.mall.tiny.modules")
+                .title("mall-tiny项目骨架")
+                .description("mall-tiny项目骨架相关接口文档")
+                .contactName("macro")
+                .version("1.0")
+                .enableSecurity(true)
+                .build();
+    }
+}

+ 62 - 0
src/main/java/com/macro/mall/tiny/domain/AdminUserDetails.java

@@ -0,0 +1,62 @@
+package com.macro.mall.tiny.domain;
+
+import com.macro.mall.tiny.modules.ums.model.UmsAdmin;
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * SpringSecurity需要的用户详情
+ * Created by macro on 2018/4/26.
+ */
+public class AdminUserDetails implements UserDetails {
+    private UmsAdmin umsAdmin;
+    private List<UmsResource> resourceList;
+    public AdminUserDetails(UmsAdmin umsAdmin, List<UmsResource> resourceList) {
+        this.umsAdmin = umsAdmin;
+        this.resourceList = resourceList;
+    }
+
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        //返回当前用户的角色
+        return resourceList.stream()
+                .map(role ->new SimpleGrantedAuthority(role.getId()+":"+role.getName()))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public String getPassword() {
+        return umsAdmin.getPassword();
+    }
+
+    @Override
+    public String getUsername() {
+        return umsAdmin.getUsername();
+    }
+
+    @Override
+    public boolean isAccountNonExpired() {
+        return true;
+    }
+
+    @Override
+    public boolean isAccountNonLocked() {
+        return true;
+    }
+
+    @Override
+    public boolean isCredentialsNonExpired() {
+        return true;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return umsAdmin.getStatus().equals(1);
+    }
+}

+ 162 - 0
src/main/java/com/macro/mall/tiny/generator/MyBatisPlusGenerator.java

@@ -0,0 +1,162 @@
+package com.macro.mall.tiny.generator;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.setting.dialect.Props;
+import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
+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.LikeTable;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import com.baomidou.mybatisplus.generator.config.rules.DateType;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+/**
+ * MyBatisPlus代码生成器
+ * Created by macro on 2020/8/20.
+ */
+public class MyBatisPlusGenerator {
+
+    public static void main(String[] args) {
+        String projectPath = System.getProperty("user.dir");
+        String moduleName = scanner("模块名");
+        String[] tableNames = scanner("表名,多个英文逗号分割").split(",");
+        // 代码生成器
+        AutoGenerator autoGenerator = new AutoGenerator();
+        autoGenerator.setGlobalConfig(initGlobalConfig(projectPath));
+        autoGenerator.setDataSource(initDataSourceConfig());
+        autoGenerator.setPackageInfo(initPackageConfig(moduleName));
+        autoGenerator.setCfg(initInjectionConfig(projectPath, moduleName));
+        autoGenerator.setTemplate(initTemplateConfig());
+        autoGenerator.setStrategy(initStrategyConfig(tableNames));
+        autoGenerator.setTemplateEngine(new VelocityTemplateEngine());
+        autoGenerator.execute();
+    }
+
+    /**
+     * 读取控制台内容信息
+     */
+    private static String scanner(String tip) {
+        Scanner scanner = new Scanner(System.in);
+        System.out.println(("请输入" + tip + ":"));
+        if (scanner.hasNext()) {
+            String next = scanner.next();
+            if (StrUtil.isNotEmpty(next)) {
+                return next;
+            }
+        }
+        throw new MybatisPlusException("请输入正确的" + tip + "!");
+    }
+
+    /**
+     * 初始化全局配置
+     */
+    private static GlobalConfig initGlobalConfig(String projectPath) {
+        GlobalConfig globalConfig = new GlobalConfig();
+        globalConfig.setOutputDir(projectPath + "/src/main/java");
+        globalConfig.setAuthor("macro");
+        globalConfig.setOpen(false);
+        globalConfig.setSwagger2(true);
+        globalConfig.setBaseResultMap(true);
+        globalConfig.setFileOverride(true);
+        globalConfig.setDateType(DateType.ONLY_DATE);
+        globalConfig.setEntityName("%s");
+        globalConfig.setMapperName("%sMapper");
+        globalConfig.setXmlName("%sMapper");
+        globalConfig.setServiceName("%sService");
+        globalConfig.setServiceImplName("%sServiceImpl");
+        globalConfig.setControllerName("%sController");
+        return globalConfig;
+    }
+
+    /**
+     * 初始化数据源配置
+     */
+    private static DataSourceConfig initDataSourceConfig() {
+        Props props = new Props("generator.properties");
+        DataSourceConfig dataSourceConfig = new DataSourceConfig();
+        dataSourceConfig.setUrl(props.getStr("dataSource.url"));
+        dataSourceConfig.setDriverName(props.getStr("dataSource.driverName"));
+        dataSourceConfig.setUsername(props.getStr("dataSource.username"));
+        dataSourceConfig.setPassword(props.getStr("dataSource.password"));
+        return dataSourceConfig;
+    }
+
+    /**
+     * 初始化包配置
+     */
+    private static PackageConfig initPackageConfig(String moduleName) {
+        Props props = new Props("generator.properties");
+        PackageConfig packageConfig = new PackageConfig();
+        packageConfig.setModuleName(moduleName);
+        packageConfig.setParent(props.getStr("package.base"));
+        packageConfig.setEntity("model");
+        return packageConfig;
+    }
+
+    /**
+     * 初始化模板配置
+     */
+    private static TemplateConfig initTemplateConfig() {
+        TemplateConfig templateConfig = new TemplateConfig();
+        //可以对controller、service、entity模板进行配置
+        //mapper.xml模板需单独配置
+        templateConfig.setXml(null);
+        return templateConfig;
+    }
+
+    /**
+     * 初始化策略配置
+     */
+    private static StrategyConfig initStrategyConfig(String[] tableNames) {
+        StrategyConfig strategyConfig = new StrategyConfig();
+        strategyConfig.setNaming(NamingStrategy.underline_to_camel);
+        strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
+        strategyConfig.setEntityLombokModel(true);
+        strategyConfig.setRestControllerStyle(true);
+        //当表名中带*号时可以启用通配符模式
+        if (tableNames.length == 1 && tableNames[0].contains("*")) {
+            String[] likeStr = tableNames[0].split("_");
+            String likePrefix = likeStr[0] + "_";
+            strategyConfig.setLikeTable(new LikeTable(likePrefix));
+        } else {
+            strategyConfig.setInclude(tableNames);
+        }
+        return strategyConfig;
+    }
+
+    /**
+     * 初始化自定义配置
+     */
+    private static InjectionConfig initInjectionConfig(String projectPath, String moduleName) {
+        // 自定义配置
+        InjectionConfig injectionConfig = new InjectionConfig() {
+            @Override
+            public void initMap() {
+                // 可用于自定义属性
+            }
+        };
+        // 模板引擎是Velocity
+        String templatePath = "/templates/mapper.xml.vm";
+        // 自定义输出配置
+        List<FileOutConfig> focList = new ArrayList<>();
+        // 自定义配置会被优先输出
+        focList.add(new FileOutConfig(templatePath) {
+            @Override
+            public String outputFile(TableInfo tableInfo) {
+                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
+                return projectPath + "/src/main/resources/mapper/" + moduleName
+                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
+            }
+        });
+        injectionConfig.setFileOutConfigList(focList);
+        return injectionConfig;
+    }
+
+}

+ 42 - 0
src/main/java/com/macro/mall/tiny/modules/business/controller/BProvinceController.java

@@ -0,0 +1,42 @@
+package com.macro.mall.tiny.modules.business.controller;
+
+
+import com.macro.mall.tiny.common.api.CommonResult;
+import com.macro.mall.tiny.modules.business.model.BProvince;
+import com.macro.mall.tiny.modules.business.service.impl.BProvinceServiceImpl;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 后台角色资源关系表 前端控制器
+ * </p>
+ *
+ * @author macro
+ * @since 2021-03-19
+ */
+@RestController
+@Api(tags = "BProvinceController", description = "省份管理")
+@RequestMapping("/province")
+public class BProvinceController {
+
+    @Autowired
+    private BProvinceServiceImpl provinceService;
+
+    @ApiOperation("获取省份列表")
+    @RequestMapping(value = "/list", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<List<BProvince>> list() {
+        List<BProvince> provinceList = provinceService.list();
+        return CommonResult.success(provinceList);
+    }
+}
+

+ 16 - 0
src/main/java/com/macro/mall/tiny/modules/business/mapper/BProvinceMapper.java

@@ -0,0 +1,16 @@
+package com.macro.mall.tiny.modules.business.mapper;
+
+import com.macro.mall.tiny.modules.business.model.BProvince;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 后台角色资源关系表 Mapper 接口
+ * </p>
+ *
+ * @author macro
+ * @since 2021-03-19
+ */
+public interface BProvinceMapper extends BaseMapper<BProvince> {
+
+}

+ 35 - 0
src/main/java/com/macro/mall/tiny/modules/business/model/BProvince.java

@@ -0,0 +1,35 @@
+package com.macro.mall.tiny.modules.business.model;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 后台角色资源关系表
+ * </p>
+ *
+ * @author macro
+ * @since 2021-03-19
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("b_province")
+@ApiModel(value="BProvince对象", description="后台角色资源关系表")
+public class BProvince implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value = "省份名称")
+    private String province;
+
+
+}

+ 18 - 0
src/main/java/com/macro/mall/tiny/modules/business/service/BProvinceCacheService.java

@@ -0,0 +1,18 @@
+package com.macro.mall.tiny.modules.business.service;
+
+
+import com.macro.mall.tiny.modules.business.model.BProvince;
+import com.macro.mall.tiny.modules.ums.model.UmsAdmin;
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+
+import java.util.List;
+
+/**
+ * 省份缓存
+ */
+public interface BProvinceCacheService {
+
+    List<BProvince> getProvinceList();
+
+    void setProvinceList(List<BProvince> provinceList);
+}

+ 19 - 0
src/main/java/com/macro/mall/tiny/modules/business/service/BProvinceService.java

@@ -0,0 +1,19 @@
+package com.macro.mall.tiny.modules.business.service;
+
+import com.macro.mall.tiny.modules.business.model.BProvince;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 后台角色资源关系表 服务类
+ * </p>
+ *
+ * @author macro
+ * @since 2021-03-19
+ */
+public interface BProvinceService extends IService<BProvince> {
+
+    List<BProvince> getProvinceList();
+}

+ 39 - 0
src/main/java/com/macro/mall/tiny/modules/business/service/impl/BProvinceCacheServiceImpl.java

@@ -0,0 +1,39 @@
+package com.macro.mall.tiny.modules.business.service.impl;
+
+import com.macro.mall.tiny.common.service.RedisService;
+import com.macro.mall.tiny.modules.business.model.BProvince;
+import com.macro.mall.tiny.modules.business.service.BProvinceCacheService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author gjs
+ * @description 省份缓存管理Service实现类
+ * @date 2021/3/19 0:32
+ */
+@Service
+public class BProvinceCacheServiceImpl implements BProvinceCacheService {
+
+    @Autowired
+    private RedisService redisService;
+    @Value("${redis.database}")
+    private String REDIS_DATABASE;
+    @Value("${redis.expire.common}")
+    private Long REDIS_EXPIRE;
+    @Value("${business:provinceList}")
+    private String REDIS_KEY_PROVINCE_LIST;
+
+    @Override
+    public List<BProvince> getProvinceList() {
+        String key = REDIS_DATABASE + ":" + REDIS_KEY_PROVINCE_LIST;
+        return (List<BProvince>) redisService.get(key);
+    }
+
+    public void setProvinceList(List<BProvince> provinceList) {
+        String key = REDIS_DATABASE + ":" + REDIS_KEY_PROVINCE_LIST;
+        redisService.set(key, provinceList, REDIS_EXPIRE);
+    }
+}

+ 44 - 0
src/main/java/com/macro/mall/tiny/modules/business/service/impl/BProvinceServiceImpl.java

@@ -0,0 +1,44 @@
+package com.macro.mall.tiny.modules.business.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.macro.mall.tiny.modules.business.model.BProvince;
+import com.macro.mall.tiny.modules.business.mapper.BProvinceMapper;
+import com.macro.mall.tiny.modules.business.service.BProvinceCacheService;
+import com.macro.mall.tiny.modules.business.service.BProvinceService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 后台角色资源关系表 服务实现类
+ * </p>
+ *
+ * @author macro
+ * @since 2021-03-19
+ */
+@Service
+public class BProvinceServiceImpl extends ServiceImpl<BProvinceMapper, BProvince> implements BProvinceService {
+
+    @Autowired
+    private BProvinceCacheService provinceCacheService;
+
+    @Autowired
+    private BProvinceMapper provinceMapper;
+
+    @Override
+    public List<BProvince> getProvinceList() {
+        List<BProvince> provinceList = provinceCacheService.getProvinceList();
+        if(CollUtil.isNotEmpty(provinceList)){
+            return  provinceList;
+        }
+        provinceList = provinceMapper.selectList(new QueryWrapper<>());
+        if(CollUtil.isNotEmpty(provinceList)){
+            provinceCacheService.setProvinceList(provinceList);
+        }
+        return provinceList;
+    }
+}

+ 204 - 0
src/main/java/com/macro/mall/tiny/modules/ums/controller/UmsAdminController.java

@@ -0,0 +1,204 @@
+package com.macro.mall.tiny.modules.ums.controller;
+
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.macro.mall.tiny.common.api.CommonPage;
+import com.macro.mall.tiny.common.api.CommonResult;
+import com.macro.mall.tiny.modules.ums.dto.UmsAdminLoginParam;
+import com.macro.mall.tiny.modules.ums.dto.UmsAdminParam;
+import com.macro.mall.tiny.modules.ums.dto.UpdateAdminPasswordParam;
+import com.macro.mall.tiny.modules.ums.model.UmsAdmin;
+import com.macro.mall.tiny.modules.ums.model.UmsRole;
+import com.macro.mall.tiny.modules.ums.service.UmsAdminService;
+import com.macro.mall.tiny.modules.ums.service.UmsRoleService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Controller;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 后台用户管理
+ * Created by macro on 2018/4/26.
+ */
+@Controller
+@Api(tags = "UmsAdminController", description = "后台用户管理")
+@RequestMapping("/admin")
+public class UmsAdminController {
+    @Value("${jwt.tokenHeader}")
+    private String tokenHeader;
+    @Value("${jwt.tokenHead}")
+    private String tokenHead;
+    @Autowired
+    private UmsAdminService adminService;
+    @Autowired
+    private UmsRoleService roleService;
+
+    @ApiOperation(value = "用户注册")
+    @RequestMapping(value = "/register", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult<UmsAdmin> register(@Validated @RequestBody UmsAdminParam umsAdminParam) {
+        UmsAdmin umsAdmin = adminService.register(umsAdminParam);
+        if (umsAdmin == null) {
+            return CommonResult.failed();
+        }
+        return CommonResult.success(umsAdmin);
+    }
+
+    @ApiOperation(value = "登录以后返回token")
+    @RequestMapping(value = "/login", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult login(@Validated @RequestBody UmsAdminLoginParam umsAdminLoginParam) {
+        String token = adminService.login(umsAdminLoginParam.getUsername(), umsAdminLoginParam.getPassword());
+        if (token == null) {
+            return CommonResult.validateFailed("用户名或密码错误");
+        }
+        Map<String, String> tokenMap = new HashMap<>();
+        tokenMap.put("token", token);
+        tokenMap.put("tokenHead", tokenHead);
+        return CommonResult.success(tokenMap);
+    }
+
+    @ApiOperation(value = "刷新token")
+    @RequestMapping(value = "/refreshToken", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult refreshToken(HttpServletRequest request) {
+        String token = request.getHeader(tokenHeader);
+        String refreshToken = adminService.refreshToken(token);
+        if (refreshToken == null) {
+            return CommonResult.failed("token已经过期!");
+        }
+        Map<String, String> tokenMap = new HashMap<>();
+        tokenMap.put("token", refreshToken);
+        tokenMap.put("tokenHead", tokenHead);
+        return CommonResult.success(tokenMap);
+    }
+
+    @ApiOperation(value = "获取当前登录用户信息")
+    @RequestMapping(value = "/info", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult getAdminInfo(Principal principal) {
+        if(principal==null){
+            return CommonResult.unauthorized(null);
+        }
+        String username = principal.getName();
+        UmsAdmin umsAdmin = adminService.getAdminByUsername(username);
+        Map<String, Object> data = new HashMap<>();
+        data.put("username", umsAdmin.getUsername());
+        data.put("menus", roleService.getMenuList(umsAdmin.getId()));
+        data.put("icon", umsAdmin.getIcon());
+        List<UmsRole> roleList = adminService.getRoleList(umsAdmin.getId());
+        if(CollUtil.isNotEmpty(roleList)){
+            List<String> roles = roleList.stream().map(UmsRole::getName).collect(Collectors.toList());
+            data.put("roles",roles);
+        }
+        return CommonResult.success(data);
+    }
+
+    @ApiOperation(value = "登出功能")
+    @RequestMapping(value = "/logout", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult logout() {
+        return CommonResult.success(null);
+    }
+
+    @ApiOperation("根据用户名或姓名分页获取用户列表")
+    @RequestMapping(value = "/list", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<CommonPage<UmsAdmin>> list(@RequestParam(value = "keyword", required = false) String keyword,
+                                                   @RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,
+                                                   @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum) {
+        Page<UmsAdmin> adminList = adminService.list(keyword, pageSize, pageNum);
+        return CommonResult.success(CommonPage.restPage(adminList));
+    }
+
+    @ApiOperation("获取指定用户信息")
+    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<UmsAdmin> getItem(@PathVariable Long id) {
+        UmsAdmin admin = adminService.getById(id);
+        return CommonResult.success(admin);
+    }
+
+    @ApiOperation("修改指定用户信息")
+    @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult update(@PathVariable Long id, @RequestBody UmsAdmin admin) {
+        boolean success = adminService.update(id, admin);
+        if (success) {
+            return CommonResult.success(null);
+        }
+        return CommonResult.failed();
+    }
+
+    @ApiOperation("修改指定用户密码")
+    @RequestMapping(value = "/updatePassword", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult updatePassword(@Validated @RequestBody UpdateAdminPasswordParam updatePasswordParam) {
+        int status = adminService.updatePassword(updatePasswordParam);
+        if (status > 0) {
+            return CommonResult.success(status);
+        } else if (status == -1) {
+            return CommonResult.failed("提交参数不合法");
+        } else if (status == -2) {
+            return CommonResult.failed("找不到该用户");
+        } else if (status == -3) {
+            return CommonResult.failed("旧密码错误");
+        } else {
+            return CommonResult.failed();
+        }
+    }
+
+    @ApiOperation("删除指定用户信息")
+    @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult delete(@PathVariable Long id) {
+        boolean success = adminService.delete(id);
+        if (success) {
+            return CommonResult.success(null);
+        }
+        return CommonResult.failed();
+    }
+
+    @ApiOperation("修改帐号状态")
+    @RequestMapping(value = "/updateStatus/{id}", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult updateStatus(@PathVariable Long id,@RequestParam(value = "status") Integer status) {
+        UmsAdmin umsAdmin = new UmsAdmin();
+        umsAdmin.setStatus(status);
+        boolean success = adminService.update(id,umsAdmin);
+        if (success) {
+            return CommonResult.success(null);
+        }
+        return CommonResult.failed();
+    }
+
+    @ApiOperation("给用户分配角色")
+    @RequestMapping(value = "/role/update", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult updateRole(@RequestParam("adminId") Long adminId,
+                                   @RequestParam("roleIds") List<Long> roleIds) {
+        int count = adminService.updateRole(adminId, roleIds);
+        if (count >= 0) {
+            return CommonResult.success(count);
+        }
+        return CommonResult.failed();
+    }
+
+    @ApiOperation("获取指定用户的角色")
+    @RequestMapping(value = "/role/{adminId}", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<List<UmsRole>> getRoleList(@PathVariable Long adminId) {
+        List<UmsRole> roleList = adminService.getRoleList(adminId);
+        return CommonResult.success(roleList);
+    }
+}

+ 103 - 0
src/main/java/com/macro/mall/tiny/modules/ums/controller/UmsMenuController.java

@@ -0,0 +1,103 @@
+//package com.macro.mall.tiny.modules.ums.controller;
+//
+//import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+//import com.macro.mall.tiny.common.api.CommonPage;
+//import com.macro.mall.tiny.common.api.CommonResult;
+//import com.macro.mall.tiny.modules.ums.dto.UmsMenuNode;
+//import com.macro.mall.tiny.modules.ums.model.UmsMenu;
+//import com.macro.mall.tiny.modules.ums.service.UmsMenuService;
+//import io.swagger.annotations.Api;
+//import io.swagger.annotations.ApiOperation;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.stereotype.Controller;
+//import org.springframework.web.bind.annotation.*;
+//
+//import java.util.List;
+//
+///**
+// * 后台菜单管理Controller
+// * Created by macro on 2020/2/4.
+// */
+//@Controller
+//@Api(tags = "UmsMenuController", description = "后台菜单管理")
+//@RequestMapping("/menu")
+//public class UmsMenuController {
+//
+//    @Autowired
+//    private UmsMenuService menuService;
+//
+//    @ApiOperation("添加后台菜单")
+//    @RequestMapping(value = "/create", method = RequestMethod.POST)
+//    @ResponseBody
+//    public CommonResult create(@RequestBody UmsMenu umsMenu) {
+//        boolean success = menuService.create(umsMenu);
+//        if (success) {
+//            return CommonResult.success(null);
+//        } else {
+//            return CommonResult.failed();
+//        }
+//    }
+//
+//    @ApiOperation("修改后台菜单")
+//    @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
+//    @ResponseBody
+//    public CommonResult update(@PathVariable Long id,
+//                               @RequestBody UmsMenu umsMenu) {
+//        boolean success = menuService.update(id, umsMenu);
+//        if (success) {
+//            return CommonResult.success(null);
+//        } else {
+//            return CommonResult.failed();
+//        }
+//    }
+//
+//    @ApiOperation("根据ID获取菜单详情")
+//    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
+//    @ResponseBody
+//    public CommonResult<UmsMenu> getItem(@PathVariable Long id) {
+//        UmsMenu umsMenu = menuService.getById(id);
+//        return CommonResult.success(umsMenu);
+//    }
+//
+//    @ApiOperation("根据ID删除后台菜单")
+//    @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
+//    @ResponseBody
+//    public CommonResult delete(@PathVariable Long id) {
+//        boolean success = menuService.removeById(id);
+//        if (success) {
+//            return CommonResult.success(null);
+//        } else {
+//            return CommonResult.failed();
+//        }
+//    }
+//
+//    @ApiOperation("分页查询后台菜单")
+//    @RequestMapping(value = "/list/{parentId}", method = RequestMethod.GET)
+//    @ResponseBody
+//    public CommonResult<CommonPage<UmsMenu>> list(@PathVariable Long parentId,
+//                                                  @RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,
+//                                                  @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum) {
+//        Page<UmsMenu> menuList = menuService.list(parentId, pageSize, pageNum);
+//        return CommonResult.success(CommonPage.restPage(menuList));
+//    }
+//
+//    @ApiOperation("树形结构返回所有菜单列表")
+//    @RequestMapping(value = "/treeList", method = RequestMethod.GET)
+//    @ResponseBody
+//    public CommonResult<List<UmsMenuNode>> treeList() {
+//        List<UmsMenuNode> list = menuService.treeList();
+//        return CommonResult.success(list);
+//    }
+//
+//    @ApiOperation("修改菜单显示状态")
+//    @RequestMapping(value = "/updateHidden/{id}", method = RequestMethod.POST)
+//    @ResponseBody
+//    public CommonResult updateHidden(@PathVariable Long id, @RequestParam("hidden") Integer hidden) {
+//        boolean success = menuService.updateHidden(id, hidden);
+//        if (success) {
+//            return CommonResult.success(null);
+//        } else {
+//            return CommonResult.failed();
+//        }
+//    }
+//}

+ 70 - 0
src/main/java/com/macro/mall/tiny/modules/ums/controller/UmsResourceCategoryController.java

@@ -0,0 +1,70 @@
+//package com.macro.mall.tiny.modules.ums.controller;
+//
+//import com.macro.mall.tiny.common.api.CommonResult;
+//import com.macro.mall.tiny.modules.ums.model.UmsResourceCategory;
+//import com.macro.mall.tiny.modules.ums.service.UmsResourceCategoryService;
+//import io.swagger.annotations.Api;
+//import io.swagger.annotations.ApiOperation;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.stereotype.Controller;
+//import org.springframework.web.bind.annotation.*;
+//
+//import java.util.List;
+//
+///**
+// * 后台资源分类管理Controller
+// * Created by macro on 2020/2/5.
+// */
+//@Controller
+//@Api(tags = "UmsResourceCategoryController", description = "后台资源分类管理")
+//@RequestMapping("/resourceCategory")
+//public class UmsResourceCategoryController {
+//    @Autowired
+//    private UmsResourceCategoryService resourceCategoryService;
+//
+//    @ApiOperation("查询所有后台资源分类")
+//    @RequestMapping(value = "/listAll", method = RequestMethod.GET)
+//    @ResponseBody
+//    public CommonResult<List<UmsResourceCategory>> listAll() {
+//        List<UmsResourceCategory> resourceList = resourceCategoryService.listAll();
+//        return CommonResult.success(resourceList);
+//    }
+//
+//    @ApiOperation("添加后台资源分类")
+//    @RequestMapping(value = "/create", method = RequestMethod.POST)
+//    @ResponseBody
+//    public CommonResult create(@RequestBody UmsResourceCategory umsResourceCategory) {
+//        boolean success = resourceCategoryService.create(umsResourceCategory);
+//        if (success) {
+//            return CommonResult.success(null);
+//        } else {
+//            return CommonResult.failed();
+//        }
+//    }
+//
+//    @ApiOperation("修改后台资源分类")
+//    @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
+//    @ResponseBody
+//    public CommonResult update(@PathVariable Long id,
+//                               @RequestBody UmsResourceCategory umsResourceCategory) {
+//        umsResourceCategory.setId(id);
+//        boolean success = resourceCategoryService.updateById(umsResourceCategory);
+//        if (success) {
+//            return CommonResult.success(null);
+//        } else {
+//            return CommonResult.failed();
+//        }
+//    }
+//
+//    @ApiOperation("根据ID删除后台资源")
+//    @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
+//    @ResponseBody
+//    public CommonResult delete(@PathVariable Long id) {
+//        boolean success = resourceCategoryService.removeById(id);
+//        if (success) {
+//            return CommonResult.success(null);
+//        } else {
+//            return CommonResult.failed();
+//        }
+//    }
+//}

+ 98 - 0
src/main/java/com/macro/mall/tiny/modules/ums/controller/UmsResourceController.java

@@ -0,0 +1,98 @@
+package com.macro.mall.tiny.modules.ums.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.macro.mall.tiny.common.api.CommonPage;
+import com.macro.mall.tiny.common.api.CommonResult;
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+import com.macro.mall.tiny.modules.ums.service.UmsResourceService;
+import com.macro.mall.tiny.security.component.DynamicSecurityMetadataSource;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 后台资源管理Controller
+ * Created by macro on 2020/2/4.
+ */
+@Controller
+@Api(tags = "UmsResourceController", description = "后台资源管理")
+@RequestMapping("/resource")
+public class UmsResourceController {
+
+    @Autowired
+    private UmsResourceService resourceService;
+    @Autowired
+    private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
+
+    @ApiOperation("添加后台资源")
+    @RequestMapping(value = "/create", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult create(@RequestBody UmsResource umsResource) {
+        boolean success = resourceService.create(umsResource);
+        dynamicSecurityMetadataSource.clearDataSource();
+        if (success) {
+            return CommonResult.success(null);
+        } else {
+            return CommonResult.failed();
+        }
+    }
+
+    @ApiOperation("修改后台资源")
+    @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult update(@PathVariable Long id,
+                               @RequestBody UmsResource umsResource) {
+        boolean success = resourceService.update(id, umsResource);
+        dynamicSecurityMetadataSource.clearDataSource();
+        if (success) {
+            return CommonResult.success(null);
+        } else {
+            return CommonResult.failed();
+        }
+    }
+
+    @ApiOperation("根据ID获取资源详情")
+    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<UmsResource> getItem(@PathVariable Long id) {
+        UmsResource umsResource = resourceService.getById(id);
+        return CommonResult.success(umsResource);
+    }
+
+    @ApiOperation("根据ID删除后台资源")
+    @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult delete(@PathVariable Long id) {
+        boolean success = resourceService.delete(id);
+        dynamicSecurityMetadataSource.clearDataSource();
+        if (success) {
+            return CommonResult.success(null);
+        } else {
+            return CommonResult.failed();
+        }
+    }
+
+    @ApiOperation("分页模糊查询后台资源")
+    @RequestMapping(value = "/list", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<CommonPage<UmsResource>> list(@RequestParam(required = false) Long categoryId,
+                                                      @RequestParam(required = false) String nameKeyword,
+                                                      @RequestParam(required = false) String urlKeyword,
+                                                      @RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,
+                                                      @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum) {
+        Page<UmsResource> resourceList = resourceService.list(categoryId,nameKeyword, urlKeyword, pageSize, pageNum);
+        return CommonResult.success(CommonPage.restPage(resourceList));
+    }
+
+    @ApiOperation("查询所有后台资源")
+    @RequestMapping(value = "/listAll", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<List<UmsResource>> listAll() {
+        List<UmsResource> resourceList = resourceService.list();
+        return CommonResult.success(resourceList);
+    }
+}

+ 128 - 0
src/main/java/com/macro/mall/tiny/modules/ums/controller/UmsRoleController.java

@@ -0,0 +1,128 @@
+package com.macro.mall.tiny.modules.ums.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.macro.mall.tiny.common.api.CommonPage;
+import com.macro.mall.tiny.common.api.CommonResult;
+import com.macro.mall.tiny.modules.ums.model.UmsMenu;
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+import com.macro.mall.tiny.modules.ums.model.UmsRole;
+import com.macro.mall.tiny.modules.ums.service.UmsRoleService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 后台用户角色管理
+ * Created by macro on 2018/9/30.
+ */
+@Controller
+@Api(tags = "UmsRoleController", description = "后台用户角色管理")
+@RequestMapping("/role")
+public class UmsRoleController {
+    @Autowired
+    private UmsRoleService roleService;
+
+    @ApiOperation("添加角色")
+    @RequestMapping(value = "/create", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult create(@RequestBody UmsRole role) {
+        boolean success = roleService.create(role);
+        if (success) {
+            return CommonResult.success(null);
+        }
+        return CommonResult.failed();
+    }
+
+    @ApiOperation("修改角色")
+    @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult update(@PathVariable Long id, @RequestBody UmsRole role) {
+        role.setId(id);
+        boolean success = roleService.updateById(role);
+        if (success) {
+            return CommonResult.success(null);
+        }
+        return CommonResult.failed();
+    }
+
+    @ApiOperation("批量删除角色")
+    @RequestMapping(value = "/delete", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult delete(@RequestParam("ids") List<Long> ids) {
+        boolean success = roleService.delete(ids);
+        if (success) {
+            return CommonResult.success(null);
+        }
+        return CommonResult.failed();
+    }
+
+
+    @ApiOperation("获取所有角色")
+    @RequestMapping(value = "/listAll", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<List<UmsRole>> listAll() {
+        List<UmsRole> roleList = roleService.list();
+        return CommonResult.success(roleList);
+    }
+
+    @ApiOperation("根据角色名称分页获取角色列表")
+    @RequestMapping(value = "/list", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<CommonPage<UmsRole>> list(@RequestParam(value = "keyword", required = false) String keyword,
+                                                  @RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,
+                                                  @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum) {
+        Page<UmsRole> roleList = roleService.list(keyword, pageSize, pageNum);
+        return CommonResult.success(CommonPage.restPage(roleList));
+    }
+
+    @ApiOperation("修改角色状态")
+    @RequestMapping(value = "/updateStatus/{id}", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult updateStatus(@PathVariable Long id, @RequestParam(value = "status") Integer status) {
+        UmsRole umsRole = new UmsRole();
+        umsRole.setId(id);
+        umsRole.setStatus(status);
+        boolean success = roleService.updateById(umsRole);
+        if (success) {
+            return CommonResult.success(null);
+        }
+        return CommonResult.failed();
+    }
+
+    /*@ApiOperation("获取角色相关菜单")
+    @RequestMapping(value = "/listMenu/{roleId}", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<List<UmsMenu>> listMenu(@PathVariable Long roleId) {
+        List<UmsMenu> roleList = roleService.listMenu(roleId);
+        return CommonResult.success(roleList);
+    }*/
+
+    @ApiOperation("获取角色相关资源")
+    @RequestMapping(value = "/listResource/{roleId}", method = RequestMethod.GET)
+    @ResponseBody
+    public CommonResult<List<UmsResource>> listResource(@PathVariable Long roleId) {
+        List<UmsResource> roleList = roleService.listResource(roleId);
+        return CommonResult.success(roleList);
+    }
+
+    /*@ApiOperation("给角色分配菜单")
+    @RequestMapping(value = "/allocMenu", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult allocMenu(@RequestParam Long roleId, @RequestParam List<Long> menuIds) {
+        int count = roleService.allocMenu(roleId, menuIds);
+        return CommonResult.success(count);
+    }*/
+
+    @ApiOperation("给角色分配资源")
+    @RequestMapping(value = "/allocResource", method = RequestMethod.POST)
+    @ResponseBody
+    public CommonResult allocResource(@RequestParam Long roleId, @RequestParam List<Long> resourceIds) {
+        int count = roleService.allocResource(roleId, resourceIds);
+        return CommonResult.success(count);
+    }
+
+}

+ 22 - 0
src/main/java/com/macro/mall/tiny/modules/ums/dto/UmsAdminLoginParam.java

@@ -0,0 +1,22 @@
+package com.macro.mall.tiny.modules.ums.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * 用户登录参数
+ * Created by macro on 2018/4/26.
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class UmsAdminLoginParam {
+    @NotEmpty
+    @ApiModelProperty(value = "用户名",required = true)
+    private String username;
+    @NotEmpty
+    @ApiModelProperty(value = "密码",required = true)
+    private String password;
+}

+ 32 - 0
src/main/java/com/macro/mall/tiny/modules/ums/dto/UmsAdminParam.java

@@ -0,0 +1,32 @@
+package com.macro.mall.tiny.modules.ums.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * 用户登录参数
+ * Created by macro on 2018/4/26.
+ */
+@Getter
+@Setter
+public class UmsAdminParam {
+    @NotEmpty
+    @ApiModelProperty(value = "用户名", required = true)
+    private String username;
+    @NotEmpty
+    @ApiModelProperty(value = "密码", required = true)
+    private String password;
+    @ApiModelProperty(value = "用户头像")
+    private String icon;
+    @Email
+    @ApiModelProperty(value = "邮箱")
+    private String email;
+    @ApiModelProperty(value = "用户昵称")
+    private String nickName;
+    @ApiModelProperty(value = "备注")
+    private String note;
+}

+ 19 - 0
src/main/java/com/macro/mall/tiny/modules/ums/dto/UmsMenuNode.java

@@ -0,0 +1,19 @@
+package com.macro.mall.tiny.modules.ums.dto;
+
+import com.macro.mall.tiny.modules.ums.model.UmsMenu;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * 后台菜单节点封装
+ * Created by macro on 2020/2/4.
+ */
+@Getter
+@Setter
+public class UmsMenuNode extends UmsMenu {
+    @ApiModelProperty(value = "子级菜单")
+    private List<UmsMenuNode> children;
+}

+ 25 - 0
src/main/java/com/macro/mall/tiny/modules/ums/dto/UpdateAdminPasswordParam.java

@@ -0,0 +1,25 @@
+package com.macro.mall.tiny.modules.ums.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotEmpty;
+
+/**
+ * 修改用户名密码参数
+ * Created by macro on 2019/10/9.
+ */
+@Getter
+@Setter
+public class UpdateAdminPasswordParam {
+    @NotEmpty
+    @ApiModelProperty(value = "用户名", required = true)
+    private String username;
+    @NotEmpty
+    @ApiModelProperty(value = "旧密码", required = true)
+    private String oldPassword;
+    @NotEmpty
+    @ApiModelProperty(value = "新密码", required = true)
+    private String newPassword;
+}

+ 16 - 0
src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsAdminLoginLogMapper.java

@@ -0,0 +1,16 @@
+package com.macro.mall.tiny.modules.ums.mapper;
+
+import com.macro.mall.tiny.modules.ums.model.UmsAdminLoginLog;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 后台用户登录日志表 Mapper 接口
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+public interface UmsAdminLoginLogMapper extends BaseMapper<UmsAdminLoginLog> {
+
+}

+ 24 - 0
src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsAdminMapper.java

@@ -0,0 +1,24 @@
+package com.macro.mall.tiny.modules.ums.mapper;
+
+import com.macro.mall.tiny.modules.ums.model.UmsAdmin;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 后台用户表 Mapper 接口
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+public interface UmsAdminMapper extends BaseMapper<UmsAdmin> {
+
+    /**
+     * 获取资源相关用户ID列表
+     */
+    List<Long> getAdminIdList(@Param("resourceId") Long resourceId);
+
+}

+ 21 - 0
src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsAdminRoleRelationMapper.java

@@ -0,0 +1,21 @@
+package com.macro.mall.tiny.modules.ums.mapper;
+
+import com.macro.mall.tiny.modules.ums.model.UmsAdminRoleRelation;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+import com.macro.mall.tiny.modules.ums.model.UmsRole;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 后台用户和角色关系表 Mapper 接口
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+public interface UmsAdminRoleRelationMapper extends BaseMapper<UmsAdminRoleRelation> {
+
+}

+ 28 - 0
src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsMenuMapper.java

@@ -0,0 +1,28 @@
+package com.macro.mall.tiny.modules.ums.mapper;
+
+import com.macro.mall.tiny.modules.ums.model.UmsMenu;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 后台菜单表 Mapper 接口
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+public interface UmsMenuMapper extends BaseMapper<UmsMenu> {
+
+    /**
+     * 根据后台用户ID获取菜单
+     */
+    List<UmsMenu> getMenuList(@Param("adminId") Long adminId);
+    /**
+     * 根据角色ID获取菜单
+     */
+    List<UmsMenu> getMenuListByRoleId(@Param("roleId") Long roleId);
+
+}

+ 16 - 0
src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsResourceCategoryMapper.java

@@ -0,0 +1,16 @@
+package com.macro.mall.tiny.modules.ums.mapper;
+
+import com.macro.mall.tiny.modules.ums.model.UmsResourceCategory;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 资源分类表 Mapper 接口
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+public interface UmsResourceCategoryMapper extends BaseMapper<UmsResourceCategory> {
+
+}

+ 29 - 0
src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsResourceMapper.java

@@ -0,0 +1,29 @@
+package com.macro.mall.tiny.modules.ums.mapper;
+
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 后台资源表 Mapper 接口
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+public interface UmsResourceMapper extends BaseMapper<UmsResource> {
+
+    /**
+     * 获取用户所有可访问资源
+     */
+    List<UmsResource> getResourceList(@Param("adminId") Long adminId);
+
+    /**
+     * 根据角色ID获取资源
+     */
+    List<UmsResource> getResourceListByRoleId(@Param("roleId") Long roleId);
+
+}

+ 24 - 0
src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsRoleMapper.java

@@ -0,0 +1,24 @@
+package com.macro.mall.tiny.modules.ums.mapper;
+
+import com.macro.mall.tiny.modules.ums.model.UmsRole;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 后台用户角色表 Mapper 接口
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+public interface UmsRoleMapper extends BaseMapper<UmsRole> {
+
+    /**
+     * 获取用户所有角色
+     */
+    List<UmsRole> getRoleList(@Param("adminId") Long adminId);
+
+}

+ 16 - 0
src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsRoleMenuRelationMapper.java

@@ -0,0 +1,16 @@
+package com.macro.mall.tiny.modules.ums.mapper;
+
+import com.macro.mall.tiny.modules.ums.model.UmsRoleMenuRelation;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ * 后台角色菜单关系表 Mapper 接口
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+public interface UmsRoleMenuRelationMapper extends BaseMapper<UmsRoleMenuRelation> {
+
+}

+ 19 - 0
src/main/java/com/macro/mall/tiny/modules/ums/mapper/UmsRoleResourceRelationMapper.java

@@ -0,0 +1,19 @@
+package com.macro.mall.tiny.modules.ums.mapper;
+
+import com.macro.mall.tiny.modules.ums.model.UmsRoleResourceRelation;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 后台角色资源关系表 Mapper 接口
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+public interface UmsRoleResourceRelationMapper extends BaseMapper<UmsRoleResourceRelation> {
+
+}

+ 58 - 0
src/main/java/com/macro/mall/tiny/modules/ums/model/UmsAdmin.java

@@ -0,0 +1,58 @@
+package com.macro.mall.tiny.modules.ums.model;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import java.util.Date;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 后台用户表
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ums_admin")
+@ApiModel(value="UmsAdmin对象", description="后台用户表")
+public class UmsAdmin implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    private String username;
+
+    private String password;
+
+    @ApiModelProperty(value = "头像")
+    private String icon;
+
+    @ApiModelProperty(value = "邮箱")
+    private String email;
+
+    @ApiModelProperty(value = "昵称")
+    private String nickName;
+
+    @ApiModelProperty(value = "备注信息")
+    private String note;
+
+    @ApiModelProperty(value = "创建时间")
+    private Date createTime;
+
+    @ApiModelProperty(value = "最后登录时间")
+    private Date loginTime;
+
+    @ApiModelProperty(value = "帐号启用状态:0->禁用;1->启用")
+    private Integer status;
+
+
+}

+ 44 - 0
src/main/java/com/macro/mall/tiny/modules/ums/model/UmsAdminLoginLog.java

@@ -0,0 +1,44 @@
+package com.macro.mall.tiny.modules.ums.model;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import java.util.Date;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 后台用户登录日志表
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ums_admin_login_log")
+@ApiModel(value="UmsAdminLoginLog对象", description="后台用户登录日志表")
+public class UmsAdminLoginLog implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    private Long adminId;
+
+    private Date createTime;
+
+    private String ip;
+
+    private String address;
+
+    @ApiModelProperty(value = "浏览器登录类型")
+    private String userAgent;
+
+
+}

+ 36 - 0
src/main/java/com/macro/mall/tiny/modules/ums/model/UmsAdminRoleRelation.java

@@ -0,0 +1,36 @@
+package com.macro.mall.tiny.modules.ums.model;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 后台用户和角色关系表
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ums_admin_role_relation")
+@ApiModel(value="UmsAdminRoleRelation对象", description="后台用户和角色关系表")
+public class UmsAdminRoleRelation implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    private Long adminId;
+
+    private Long roleId;
+
+
+}

+ 57 - 0
src/main/java/com/macro/mall/tiny/modules/ums/model/UmsMenu.java

@@ -0,0 +1,57 @@
+package com.macro.mall.tiny.modules.ums.model;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import java.util.Date;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 后台菜单表
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ums_menu")
+@ApiModel(value="UmsMenu对象", description="后台菜单表")
+public class UmsMenu implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "父级ID")
+    private Long parentId;
+
+    @ApiModelProperty(value = "创建时间")
+    private Date createTime;
+
+    @ApiModelProperty(value = "菜单名称")
+    private String title;
+
+    @ApiModelProperty(value = "菜单级数")
+    private Integer level;
+
+    @ApiModelProperty(value = "菜单排序")
+    private Integer sort;
+
+    @ApiModelProperty(value = "前端名称")
+    private String name;
+
+    @ApiModelProperty(value = "前端图标")
+    private String icon;
+
+    @ApiModelProperty(value = "前端隐藏")
+    private Integer hidden;
+
+
+}

+ 48 - 0
src/main/java/com/macro/mall/tiny/modules/ums/model/UmsResource.java

@@ -0,0 +1,48 @@
+package com.macro.mall.tiny.modules.ums.model;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import java.util.Date;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 后台资源表
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ums_resource")
+@ApiModel(value="UmsResource对象", description="后台资源表")
+public class UmsResource implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "创建时间")
+    private Date createTime;
+
+    @ApiModelProperty(value = "资源名称")
+    private String name;
+
+    @ApiModelProperty(value = "资源URL")
+    private String url;
+
+    @ApiModelProperty(value = "描述")
+    private String description;
+
+    @ApiModelProperty(value = "资源分类ID")
+    private Long categoryId;
+
+
+}

+ 42 - 0
src/main/java/com/macro/mall/tiny/modules/ums/model/UmsResourceCategory.java

@@ -0,0 +1,42 @@
+package com.macro.mall.tiny.modules.ums.model;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import java.util.Date;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 资源分类表
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ums_resource_category")
+@ApiModel(value="UmsResourceCategory对象", description="资源分类表")
+public class UmsResourceCategory implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "创建时间")
+    private Date createTime;
+
+    @ApiModelProperty(value = "分类名称")
+    private String name;
+
+    @ApiModelProperty(value = "排序")
+    private Integer sort;
+
+
+}

+ 50 - 0
src/main/java/com/macro/mall/tiny/modules/ums/model/UmsRole.java

@@ -0,0 +1,50 @@
+package com.macro.mall.tiny.modules.ums.model;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import java.util.Date;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 后台用户角色表
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ums_role")
+@ApiModel(value="UmsRole对象", description="后台用户角色表")
+public class UmsRole implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "名称")
+    private String name;
+
+    @ApiModelProperty(value = "描述")
+    private String description;
+
+    @ApiModelProperty(value = "后台用户数量")
+    private Integer adminCount;
+
+    @ApiModelProperty(value = "创建时间")
+    private Date createTime;
+
+    @ApiModelProperty(value = "启用状态:0->禁用;1->启用")
+    private Integer status;
+
+    private Integer sort;
+
+
+}

+ 38 - 0
src/main/java/com/macro/mall/tiny/modules/ums/model/UmsRoleMenuRelation.java

@@ -0,0 +1,38 @@
+package com.macro.mall.tiny.modules.ums.model;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 后台角色菜单关系表
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ums_role_menu_relation")
+@ApiModel(value="UmsRoleMenuRelation对象", description="后台角色菜单关系表")
+public class UmsRoleMenuRelation implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "角色ID")
+    private Long roleId;
+
+    @ApiModelProperty(value = "菜单ID")
+    private Long menuId;
+
+
+}

+ 38 - 0
src/main/java/com/macro/mall/tiny/modules/ums/model/UmsRoleResourceRelation.java

@@ -0,0 +1,38 @@
+package com.macro.mall.tiny.modules.ums.model;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.io.Serializable;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 后台角色资源关系表
+ * </p>
+ *
+ * @author macro
+ * @since 2020-08-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("ums_role_resource_relation")
+@ApiModel(value="UmsRoleResourceRelation对象", description="后台角色资源关系表")
+public class UmsRoleResourceRelation implements Serializable {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "角色ID")
+    private Long roleId;
+
+    @ApiModelProperty(value = "资源ID")
+    private Long resourceId;
+
+
+}

+ 59 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/UmsAdminCacheService.java

@@ -0,0 +1,59 @@
+package com.macro.mall.tiny.modules.ums.service;
+
+
+
+import com.macro.mall.tiny.modules.ums.model.UmsAdmin;
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+
+import java.util.List;
+
+/**
+ * 后台用户缓存管理Service
+ * Created by macro on 2020/3/13.
+ */
+public interface UmsAdminCacheService {
+    /**
+     * 删除后台用户缓存
+     */
+    void delAdmin(Long adminId);
+
+    /**
+     * 删除后台用户资源列表缓存
+     */
+    void delResourceList(Long adminId);
+
+    /**
+     * 当角色相关资源信息改变时删除相关后台用户缓存
+     */
+    void delResourceListByRole(Long roleId);
+
+    /**
+     * 当角色相关资源信息改变时删除相关后台用户缓存
+     */
+    void delResourceListByRoleIds(List<Long> roleIds);
+
+    /**
+     * 当资源信息改变时,删除资源项目后台用户缓存
+     */
+    void delResourceListByResource(Long resourceId);
+
+    /**
+     * 获取缓存后台用户信息
+     */
+    UmsAdmin getAdmin(String username);
+
+    /**
+     * 设置缓存后台用户信息
+     */
+    void setAdmin(UmsAdmin admin);
+
+    /**
+     * 获取缓存后台用户资源列表
+     */
+    List<UmsResource> getResourceList(Long adminId);
+
+    /**
+     * 设置后台后台用户资源列表
+     */
+    void setResourceList(Long adminId, List<UmsResource> resourceList);
+}

+ 11 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/UmsAdminRoleRelationService.java

@@ -0,0 +1,11 @@
+package com.macro.mall.tiny.modules.ums.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.macro.mall.tiny.modules.ums.model.UmsAdminRoleRelation;
+
+/**
+ * 管理员角色关系管理Service
+ * Created by macro on 2020/8/21.
+ */
+public interface UmsAdminRoleRelationService extends IService<UmsAdminRoleRelation> {
+}

+ 84 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/UmsAdminService.java

@@ -0,0 +1,84 @@
+package com.macro.mall.tiny.modules.ums.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.macro.mall.tiny.modules.ums.dto.UmsAdminParam;
+import com.macro.mall.tiny.modules.ums.dto.UpdateAdminPasswordParam;
+import com.macro.mall.tiny.modules.ums.model.UmsAdmin;
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+import com.macro.mall.tiny.modules.ums.model.UmsRole;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 后台管理员管理Service
+ * Created by macro on 2018/4/26.
+ */
+public interface UmsAdminService extends IService<UmsAdmin> {
+    /**
+     * 根据用户名获取后台管理员
+     */
+    UmsAdmin getAdminByUsername(String username);
+
+    /**
+     * 注册功能
+     */
+    UmsAdmin register(UmsAdminParam umsAdminParam);
+
+    /**
+     * 登录功能
+     * @param username 用户名
+     * @param password 密码
+     * @return 生成的JWT的token
+     */
+    String login(String username,String password);
+
+    /**
+     * 刷新token的功能
+     * @param oldToken 旧的token
+     */
+    String refreshToken(String oldToken);
+
+    /**
+     * 根据用户名或昵称分页查询用户
+     */
+    Page<UmsAdmin> list(String keyword, Integer pageSize, Integer pageNum);
+
+    /**
+     * 修改指定用户信息
+     */
+    boolean update(Long id, UmsAdmin admin);
+
+    /**
+     * 删除指定用户
+     */
+    boolean delete(Long id);
+
+    /**
+     * 修改用户角色关系
+     */
+    @Transactional
+    int updateRole(Long adminId, List<Long> roleIds);
+
+    /**
+     * 获取用户对于角色
+     */
+    List<UmsRole> getRoleList(Long adminId);
+
+    /**
+     * 获取指定用户的可访问资源
+     */
+    List<UmsResource> getResourceList(Long adminId);
+
+    /**
+     * 修改密码
+     */
+    int updatePassword(UpdateAdminPasswordParam updatePasswordParam);
+
+    /**
+     * 获取用户信息
+     */
+    UserDetails loadUserByUsername(String username);
+}

+ 40 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/UmsMenuService.java

@@ -0,0 +1,40 @@
+package com.macro.mall.tiny.modules.ums.service;
+
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.macro.mall.tiny.modules.ums.dto.UmsMenuNode;
+import com.macro.mall.tiny.modules.ums.model.UmsMenu;
+
+import java.util.List;
+
+/**
+ * 后台菜单管理Service
+ * Created by macro on 2020/2/2.
+ */
+public interface UmsMenuService extends IService<UmsMenu> {
+    /**
+     * 创建后台菜单
+     */
+    boolean create(UmsMenu umsMenu);
+
+    /**
+     * 修改后台菜单
+     */
+    boolean update(Long id, UmsMenu umsMenu);
+
+    /**
+     * 分页查询后台菜单
+     */
+    Page<UmsMenu> list(Long parentId, Integer pageSize, Integer pageNum);
+
+    /**
+     * 树形结构返回所有菜单列表
+     */
+    List<UmsMenuNode> treeList();
+
+    /**
+     * 修改菜单显示状态
+     */
+    boolean updateHidden(Long id, Integer hidden);
+}

+ 24 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/UmsResourceCategoryService.java

@@ -0,0 +1,24 @@
+package com.macro.mall.tiny.modules.ums.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.macro.mall.tiny.modules.ums.model.UmsResourceCategory;
+
+import java.util.List;
+
+/**
+ * 后台资源分类管理Service
+ * Created by macro on 2020/2/5.
+ */
+public interface UmsResourceCategoryService extends IService<UmsResourceCategory> {
+
+    /**
+     * 获取所有资源分类
+     */
+    List<UmsResourceCategory> listAll();
+
+    /**
+     * 创建资源分类
+     */
+    boolean create(UmsResourceCategory umsResourceCategory);
+}

+ 34 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/UmsResourceService.java

@@ -0,0 +1,34 @@
+package com.macro.mall.tiny.modules.ums.service;
+
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+
+import java.util.List;
+
+/**
+ * 后台资源管理Service
+ * Created by macro on 2020/2/2.
+ */
+public interface UmsResourceService extends IService<UmsResource> {
+    /**
+     * 添加资源
+     */
+    boolean create(UmsResource umsResource);
+
+    /**
+     * 修改资源
+     */
+    boolean update(Long id, UmsResource umsResource);
+
+    /**
+     * 删除资源
+     */
+    boolean delete(Long id);
+
+    /**
+     * 分页查询资源
+     */
+    Page<UmsResource> list(Long categoryId, String nameKeyword, String urlKeyword, Integer pageSize, Integer pageNum);
+}

+ 11 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/UmsRoleMenuRelationService.java

@@ -0,0 +1,11 @@
+package com.macro.mall.tiny.modules.ums.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.macro.mall.tiny.modules.ums.model.UmsRoleMenuRelation;
+
+/**
+ * 角色菜单关系管理Service
+ * Created by macro on 2020/8/21.
+ */
+public interface UmsRoleMenuRelationService extends IService<UmsRoleMenuRelation> {
+}

+ 11 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/UmsRoleResourceRelationService.java

@@ -0,0 +1,11 @@
+package com.macro.mall.tiny.modules.ums.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.macro.mall.tiny.modules.ums.model.UmsRoleResourceRelation;
+
+/**
+ * 角色资源关系管理Service
+ * Created by macro on 2020/8/21.
+ */
+public interface UmsRoleResourceRelationService extends IService<UmsRoleResourceRelation> {
+}

+ 58 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/UmsRoleService.java

@@ -0,0 +1,58 @@
+package com.macro.mall.tiny.modules.ums.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.macro.mall.tiny.modules.ums.model.UmsMenu;
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+import com.macro.mall.tiny.modules.ums.model.UmsRole;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 后台角色管理Service
+ * Created by macro on 2018/9/30.
+ */
+public interface UmsRoleService extends IService<UmsRole> {
+    /**
+     * 添加角色
+     */
+    boolean create(UmsRole role);
+
+    /**
+     * 批量删除角色
+     */
+    boolean delete(List<Long> ids);
+
+    /**
+     * 分页获取角色列表
+     */
+    Page<UmsRole> list(String keyword, Integer pageSize, Integer pageNum);
+
+    /**
+     * 根据管理员ID获取对应菜单
+     */
+    List<UmsMenu> getMenuList(Long adminId);
+
+    /**
+     * 获取角色相关菜单
+     */
+    List<UmsMenu> listMenu(Long roleId);
+
+    /**
+     * 获取角色相关资源
+     */
+    List<UmsResource> listResource(Long roleId);
+
+    /**
+     * 给角色分配菜单
+     */
+    @Transactional
+    int allocMenu(Long roleId, List<Long> menuIds);
+
+    /**
+     * 给角色分配资源
+     */
+    @Transactional
+    int allocResource(Long roleId, List<Long> resourceIds);
+}

+ 115 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsAdminCacheServiceImpl.java

@@ -0,0 +1,115 @@
+package com.macro.mall.tiny.modules.ums.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.macro.mall.tiny.common.service.RedisService;
+import com.macro.mall.tiny.modules.ums.mapper.UmsAdminMapper;
+import com.macro.mall.tiny.modules.ums.model.UmsAdmin;
+import com.macro.mall.tiny.modules.ums.model.UmsAdminRoleRelation;
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+import com.macro.mall.tiny.modules.ums.service.UmsAdminCacheService;
+import com.macro.mall.tiny.modules.ums.service.UmsAdminRoleRelationService;
+import com.macro.mall.tiny.modules.ums.service.UmsAdminService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 后台用户缓存管理Service实现类
+ * Created by macro on 2020/3/13.
+ */
+@Service
+public class UmsAdminCacheServiceImpl implements UmsAdminCacheService {
+    @Autowired
+    private UmsAdminService adminService;
+    @Autowired
+    private RedisService redisService;
+    @Autowired
+    private UmsAdminMapper adminMapper;
+    @Autowired
+    private UmsAdminRoleRelationService adminRoleRelationService;
+    @Value("${redis.database}")
+    private String REDIS_DATABASE;
+    @Value("${redis.expire.common}")
+    private Long REDIS_EXPIRE;
+    @Value("${redis.key.admin}")
+    private String REDIS_KEY_ADMIN;
+    @Value("${redis.key.resourceList}")
+    private String REDIS_KEY_RESOURCE_LIST;
+
+    @Override
+    public void delAdmin(Long adminId) {
+        UmsAdmin admin = adminService.getById(adminId);
+        if (admin != null) {
+            String key = REDIS_DATABASE + ":" + REDIS_KEY_ADMIN + ":" + admin.getUsername();
+            redisService.del(key);
+        }
+    }
+
+    @Override
+    public void delResourceList(Long adminId) {
+        String key = REDIS_DATABASE + ":" + REDIS_KEY_RESOURCE_LIST + ":" + adminId;
+        redisService.del(key);
+    }
+
+    @Override
+    public void delResourceListByRole(Long roleId) {
+        QueryWrapper<UmsAdminRoleRelation> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(UmsAdminRoleRelation::getRoleId,roleId);
+        List<UmsAdminRoleRelation> relationList = adminRoleRelationService.list(wrapper);
+        if (CollUtil.isNotEmpty(relationList)) {
+            String keyPrefix = REDIS_DATABASE + ":" + REDIS_KEY_RESOURCE_LIST + ":";
+            List<String> keys = relationList.stream().map(relation -> keyPrefix + relation.getAdminId()).collect(Collectors.toList());
+            redisService.del(keys);
+        }
+    }
+
+    @Override
+    public void delResourceListByRoleIds(List<Long> roleIds) {
+        QueryWrapper<UmsAdminRoleRelation> wrapper = new QueryWrapper<>();
+        wrapper.lambda().in(UmsAdminRoleRelation::getRoleId,roleIds);
+        List<UmsAdminRoleRelation> relationList = adminRoleRelationService.list(wrapper);
+        if (CollUtil.isNotEmpty(relationList)) {
+            String keyPrefix = REDIS_DATABASE + ":" + REDIS_KEY_RESOURCE_LIST + ":";
+            List<String> keys = relationList.stream().map(relation -> keyPrefix + relation.getAdminId()).collect(Collectors.toList());
+            redisService.del(keys);
+        }
+    }
+
+    @Override
+    public void delResourceListByResource(Long resourceId) {
+        List<Long> adminIdList = adminMapper.getAdminIdList(resourceId);
+        if (CollUtil.isNotEmpty(adminIdList)) {
+            String keyPrefix = REDIS_DATABASE + ":" + REDIS_KEY_RESOURCE_LIST + ":";
+            List<String> keys = adminIdList.stream().map(adminId -> keyPrefix + adminId).collect(Collectors.toList());
+            redisService.del(keys);
+        }
+    }
+
+    @Override
+    public UmsAdmin getAdmin(String username) {
+        String key = REDIS_DATABASE + ":" + REDIS_KEY_ADMIN + ":" + username;
+        return (UmsAdmin) redisService.get(key);
+    }
+
+    @Override
+    public void setAdmin(UmsAdmin admin) {
+        String key = REDIS_DATABASE + ":" + REDIS_KEY_ADMIN + ":" + admin.getUsername();
+        redisService.set(key, admin, REDIS_EXPIRE);
+    }
+
+    @Override
+    public List<UmsResource> getResourceList(Long adminId) {
+        String key = REDIS_DATABASE + ":" + REDIS_KEY_RESOURCE_LIST + ":" + adminId;
+        return (List<UmsResource>) redisService.get(key);
+    }
+
+    @Override
+    public void setResourceList(Long adminId, List<UmsResource> resourceList) {
+        String key = REDIS_DATABASE + ":" + REDIS_KEY_RESOURCE_LIST + ":" + adminId;
+        redisService.set(key, resourceList, REDIS_EXPIRE);
+    }
+}

+ 15 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsAdminRoleRelationServiceImpl.java

@@ -0,0 +1,15 @@
+package com.macro.mall.tiny.modules.ums.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.macro.mall.tiny.modules.ums.mapper.UmsAdminRoleRelationMapper;
+import com.macro.mall.tiny.modules.ums.model.UmsAdminRoleRelation;
+import com.macro.mall.tiny.modules.ums.service.UmsAdminRoleRelationService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 管理员角色关系管理Service实现类
+ * Created by macro on 2020/8/21.
+ */
+@Service
+public class UmsAdminRoleRelationServiceImpl extends ServiceImpl<UmsAdminRoleRelationMapper, UmsAdminRoleRelation> implements UmsAdminRoleRelationService {
+}

+ 267 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsAdminServiceImpl.java

@@ -0,0 +1,267 @@
+package com.macro.mall.tiny.modules.ums.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.macro.mall.tiny.common.exception.Asserts;
+import com.macro.mall.tiny.domain.AdminUserDetails;
+import com.macro.mall.tiny.modules.ums.dto.UmsAdminParam;
+import com.macro.mall.tiny.modules.ums.dto.UpdateAdminPasswordParam;
+import com.macro.mall.tiny.modules.ums.mapper.UmsAdminLoginLogMapper;
+import com.macro.mall.tiny.modules.ums.mapper.UmsAdminMapper;
+import com.macro.mall.tiny.modules.ums.mapper.UmsResourceMapper;
+import com.macro.mall.tiny.modules.ums.mapper.UmsRoleMapper;
+import com.macro.mall.tiny.modules.ums.model.*;
+import com.macro.mall.tiny.modules.ums.service.UmsAdminCacheService;
+import com.macro.mall.tiny.modules.ums.service.UmsAdminRoleRelationService;
+import com.macro.mall.tiny.modules.ums.service.UmsAdminService;
+import com.macro.mall.security.util.JwtTokenUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 后台管理员管理Service实现类
+ * Created by macro on 2018/4/26.
+ */
+@Service
+public class UmsAdminServiceImpl extends ServiceImpl<UmsAdminMapper,UmsAdmin> implements UmsAdminService {
+    private static final Logger LOGGER = LoggerFactory.getLogger(UmsAdminServiceImpl.class);
+    @Autowired
+    private JwtTokenUtil jwtTokenUtil;
+    @Autowired
+    private PasswordEncoder passwordEncoder;
+    @Autowired
+    private UmsAdminLoginLogMapper loginLogMapper;
+    @Autowired
+    private UmsAdminCacheService adminCacheService;
+    @Autowired
+    private UmsAdminRoleRelationService adminRoleRelationService;
+    @Autowired
+    private UmsRoleMapper roleMapper;
+    @Autowired
+    private UmsResourceMapper resourceMapper;
+
+    @Override
+    public UmsAdmin getAdminByUsername(String username) {
+        UmsAdmin admin = adminCacheService.getAdmin(username);
+        if(admin!=null) return  admin;
+        QueryWrapper<UmsAdmin> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(UmsAdmin::getUsername,username);
+        List<UmsAdmin> adminList = list(wrapper);
+        if (adminList != null && adminList.size() > 0) {
+            admin = adminList.get(0);
+            adminCacheService.setAdmin(admin);
+            return admin;
+        }
+        return null;
+    }
+
+    @Override
+    public UmsAdmin register(UmsAdminParam umsAdminParam) {
+        UmsAdmin umsAdmin = new UmsAdmin();
+        BeanUtils.copyProperties(umsAdminParam, umsAdmin);
+        umsAdmin.setCreateTime(new Date());
+        umsAdmin.setStatus(1);
+        //查询是否有相同用户名的用户
+        QueryWrapper<UmsAdmin> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(UmsAdmin::getUsername,umsAdmin.getUsername());
+        List<UmsAdmin> umsAdminList = list(wrapper);
+        if (umsAdminList.size() > 0) {
+            return null;
+        }
+        //将密码进行加密操作
+        String encodePassword = passwordEncoder.encode(umsAdmin.getPassword());
+        umsAdmin.setPassword(encodePassword);
+        baseMapper.insert(umsAdmin);
+        return umsAdmin;
+    }
+
+    @Override
+    public String login(String username, String password) {
+        String token = null;
+        //密码需要客户端加密后传递
+        try {
+            UserDetails userDetails = loadUserByUsername(username);
+            if(!passwordEncoder.matches(password,userDetails.getPassword())){
+                Asserts.fail("密码不正确");
+            }
+            if(!userDetails.isEnabled()){
+                Asserts.fail("帐号已被禁用");
+            }
+            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+            SecurityContextHolder.getContext().setAuthentication(authentication);
+            token = jwtTokenUtil.generateToken(userDetails);
+//            updateLoginTimeByUsername(username);
+            insertLoginLog(username);
+        } catch (AuthenticationException e) {
+            LOGGER.warn("登录异常:{}", e.getMessage());
+        }
+        return token;
+    }
+
+    /**
+     * 添加登录记录
+     * @param username 用户名
+     */
+    private void insertLoginLog(String username) {
+        UmsAdmin admin = getAdminByUsername(username);
+        if(admin==null) return;
+        UmsAdminLoginLog loginLog = new UmsAdminLoginLog();
+        loginLog.setAdminId(admin.getId());
+        loginLog.setCreateTime(new Date());
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        HttpServletRequest request = attributes.getRequest();
+        loginLog.setIp(request.getRemoteAddr());
+        loginLogMapper.insert(loginLog);
+    }
+
+    /**
+     * 根据用户名修改登录时间
+     */
+    private void updateLoginTimeByUsername(String username) {
+        UmsAdmin record = new UmsAdmin();
+        record.setLoginTime(new Date());
+        QueryWrapper<UmsAdmin> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(UmsAdmin::getUsername,username);
+        update(record,wrapper);
+    }
+
+    @Override
+    public String refreshToken(String oldToken) {
+        return jwtTokenUtil.refreshHeadToken(oldToken);
+    }
+
+    @Override
+    public Page<UmsAdmin> list(String keyword, Integer pageSize, Integer pageNum) {
+        Page<UmsAdmin> page = new Page<>(pageNum,pageSize);
+        QueryWrapper<UmsAdmin> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<UmsAdmin> lambda = wrapper.lambda();
+        if(StrUtil.isNotEmpty(keyword)){
+            lambda.like(UmsAdmin::getUsername,keyword);
+            lambda.or().like(UmsAdmin::getNickName,keyword);
+        }
+        return page(page,wrapper);
+    }
+
+    @Override
+    public boolean update(Long id, UmsAdmin admin) {
+        admin.setId(id);
+        UmsAdmin rawAdmin = getById(id);
+        if(rawAdmin.getPassword().equals(admin.getPassword())){
+            //与原加密密码相同的不需要修改
+            admin.setPassword(null);
+        }else{
+            //与原加密密码不同的需要加密修改
+            if(StrUtil.isEmpty(admin.getPassword())){
+                admin.setPassword(null);
+            }else{
+                admin.setPassword(passwordEncoder.encode(admin.getPassword()));
+            }
+        }
+        boolean success = updateById(admin);
+        adminCacheService.delAdmin(id);
+        return success;
+    }
+
+    @Override
+    public boolean delete(Long id) {
+        adminCacheService.delAdmin(id);
+        boolean success = removeById(id);
+        adminCacheService.delResourceList(id);
+        return success;
+    }
+
+    @Override
+    public int updateRole(Long adminId, List<Long> roleIds) {
+        int count = roleIds == null ? 0 : roleIds.size();
+        //先删除原来的关系
+        QueryWrapper<UmsAdminRoleRelation> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(UmsAdminRoleRelation::getAdminId,adminId);
+        adminRoleRelationService.remove(wrapper);
+        //建立新关系
+        if (!CollectionUtils.isEmpty(roleIds)) {
+            List<UmsAdminRoleRelation> list = new ArrayList<>();
+            for (Long roleId : roleIds) {
+                UmsAdminRoleRelation roleRelation = new UmsAdminRoleRelation();
+                roleRelation.setAdminId(adminId);
+                roleRelation.setRoleId(roleId);
+                list.add(roleRelation);
+            }
+            adminRoleRelationService.saveBatch(list);
+        }
+        adminCacheService.delResourceList(adminId);
+        return count;
+    }
+
+    @Override
+    public List<UmsRole> getRoleList(Long adminId) {
+        return roleMapper.getRoleList(adminId);
+    }
+
+    @Override
+    public List<UmsResource> getResourceList(Long adminId) {
+        List<UmsResource> resourceList = adminCacheService.getResourceList(adminId);
+        if(CollUtil.isNotEmpty(resourceList)){
+            return  resourceList;
+        }
+        resourceList = resourceMapper.getResourceList(adminId);
+        if(CollUtil.isNotEmpty(resourceList)){
+            adminCacheService.setResourceList(adminId,resourceList);
+        }
+        return resourceList;
+    }
+
+    @Override
+    public int updatePassword(UpdateAdminPasswordParam param) {
+        if(StrUtil.isEmpty(param.getUsername())
+                ||StrUtil.isEmpty(param.getOldPassword())
+                ||StrUtil.isEmpty(param.getNewPassword())){
+            return -1;
+        }
+        QueryWrapper<UmsAdmin> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(UmsAdmin::getUsername,param.getUsername());
+        List<UmsAdmin> adminList = list(wrapper);
+        if(CollUtil.isEmpty(adminList)){
+            return -2;
+        }
+        UmsAdmin umsAdmin = adminList.get(0);
+        if(!passwordEncoder.matches(param.getOldPassword(),umsAdmin.getPassword())){
+            return -3;
+        }
+        umsAdmin.setPassword(passwordEncoder.encode(param.getNewPassword()));
+        updateById(umsAdmin);
+        adminCacheService.delAdmin(umsAdmin.getId());
+        return 1;
+    }
+
+    @Override
+    public UserDetails loadUserByUsername(String username){
+        //获取用户信息
+        UmsAdmin admin = getAdminByUsername(username);
+        if (admin != null) {
+            List<UmsResource> resourceList = getResourceList(admin.getId());
+            return new AdminUserDetails(admin,resourceList);
+        }
+        throw new UsernameNotFoundException("用户名或密码错误");
+    }
+}

+ 94 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsMenuServiceImpl.java

@@ -0,0 +1,94 @@
+package com.macro.mall.tiny.modules.ums.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.macro.mall.tiny.modules.ums.dto.UmsMenuNode;
+import com.macro.mall.tiny.modules.ums.mapper.UmsMenuMapper;
+import com.macro.mall.tiny.modules.ums.model.UmsMenu;
+import com.macro.mall.tiny.modules.ums.service.UmsMenuService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 后台菜单管理Service实现类
+ * Created by macro on 2020/2/2.
+ */
+@Service
+public class UmsMenuServiceImpl extends ServiceImpl<UmsMenuMapper,UmsMenu>implements UmsMenuService {
+
+    @Override
+    public boolean create(UmsMenu umsMenu) {
+        umsMenu.setCreateTime(new Date());
+        updateLevel(umsMenu);
+        return save(umsMenu);
+    }
+
+    /**
+     * 修改菜单层级
+     */
+    private void updateLevel(UmsMenu umsMenu) {
+        if (umsMenu.getParentId() == 0) {
+            //没有父菜单时为一级菜单
+            umsMenu.setLevel(0);
+        } else {
+            //有父菜单时选择根据父菜单level设置
+            UmsMenu parentMenu = getById(umsMenu.getParentId());
+            if (parentMenu != null) {
+                umsMenu.setLevel(parentMenu.getLevel() + 1);
+            } else {
+                umsMenu.setLevel(0);
+            }
+        }
+    }
+
+    @Override
+    public boolean update(Long id, UmsMenu umsMenu) {
+        umsMenu.setId(id);
+        updateLevel(umsMenu);
+        return updateById(umsMenu);
+    }
+
+    @Override
+    public Page<UmsMenu> list(Long parentId, Integer pageSize, Integer pageNum) {
+        Page<UmsMenu> page = new Page<>(pageNum,pageSize);
+        QueryWrapper<UmsMenu> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(UmsMenu::getParentId,parentId)
+                .orderByDesc(UmsMenu::getSort);
+        return page(page,wrapper);
+    }
+
+    @Override
+    public List<UmsMenuNode> treeList() {
+        List<UmsMenu> menuList = list();
+        List<UmsMenuNode> result = menuList.stream()
+                .filter(menu -> menu.getParentId().equals(0L))
+                .map(menu -> covertMenuNode(menu, menuList)).collect(Collectors.toList());
+        return result;
+    }
+
+    @Override
+    public boolean updateHidden(Long id, Integer hidden) {
+        UmsMenu umsMenu = new UmsMenu();
+        umsMenu.setId(id);
+        umsMenu.setHidden(hidden);
+        return updateById(umsMenu);
+    }
+
+    /**
+     * 将UmsMenu转化为UmsMenuNode并设置children属性
+     */
+    private UmsMenuNode covertMenuNode(UmsMenu menu, List<UmsMenu> menuList) {
+        UmsMenuNode node = new UmsMenuNode();
+        BeanUtils.copyProperties(menu, node);
+        List<UmsMenuNode> children = menuList.stream()
+                .filter(subMenu -> subMenu.getParentId().equals(menu.getId()))
+                .map(subMenu -> covertMenuNode(subMenu, menuList)).collect(Collectors.toList());
+        node.setChildren(children);
+        return node;
+    }
+}

+ 32 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsResourceCategoryServiceImpl.java

@@ -0,0 +1,32 @@
+package com.macro.mall.tiny.modules.ums.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.macro.mall.tiny.modules.ums.mapper.UmsResourceCategoryMapper;
+import com.macro.mall.tiny.modules.ums.model.UmsResourceCategory;
+import com.macro.mall.tiny.modules.ums.service.UmsResourceCategoryService;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 后台资源分类管理Service实现类
+ * Created by macro on 2020/2/5.
+ */
+@Service
+public class UmsResourceCategoryServiceImpl extends ServiceImpl<UmsResourceCategoryMapper,UmsResourceCategory> implements UmsResourceCategoryService {
+
+    @Override
+    public List<UmsResourceCategory> listAll() {
+        QueryWrapper<UmsResourceCategory> wrapper = new QueryWrapper<>();
+        wrapper.lambda().orderByDesc(UmsResourceCategory::getSort);
+        return list(wrapper);
+    }
+
+    @Override
+    public boolean create(UmsResourceCategory umsResourceCategory) {
+        umsResourceCategory.setCreateTime(new Date());
+        return save(umsResourceCategory);
+    }
+}

+ 62 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsResourceServiceImpl.java

@@ -0,0 +1,62 @@
+package com.macro.mall.tiny.modules.ums.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.macro.mall.tiny.modules.ums.mapper.UmsResourceMapper;
+import com.macro.mall.tiny.modules.ums.model.UmsResource;
+import com.macro.mall.tiny.modules.ums.service.UmsAdminCacheService;
+import com.macro.mall.tiny.modules.ums.service.UmsResourceService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+
+/**
+ * 后台资源管理Service实现类
+ * Created by macro on 2020/2/2.
+ */
+@Service
+public class UmsResourceServiceImpl extends ServiceImpl<UmsResourceMapper,UmsResource>implements UmsResourceService {
+    @Autowired
+    private UmsAdminCacheService adminCacheService;
+    @Override
+    public boolean create(UmsResource umsResource) {
+        umsResource.setCreateTime(new Date());
+        return save(umsResource);
+    }
+
+    @Override
+    public boolean update(Long id, UmsResource umsResource) {
+        umsResource.setId(id);
+        boolean success = updateById(umsResource);
+        adminCacheService.delResourceListByResource(id);
+        return success;
+    }
+
+    @Override
+    public boolean delete(Long id) {
+        boolean success = removeById(id);
+        adminCacheService.delResourceListByResource(id);
+        return success;
+    }
+
+    @Override
+    public Page<UmsResource> list(Long categoryId, String nameKeyword, String urlKeyword, Integer pageSize, Integer pageNum) {
+        Page<UmsResource> page = new Page<>(pageNum,pageSize);
+        QueryWrapper<UmsResource> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<UmsResource> lambda = wrapper.lambda();
+        if(categoryId!=null){
+            lambda.eq(UmsResource::getCategoryId,categoryId);
+        }
+        if(StrUtil.isNotEmpty(nameKeyword)){
+            lambda.like(UmsResource::getName,nameKeyword);
+        }
+        if(StrUtil.isNotEmpty(urlKeyword)){
+            lambda.like(UmsResource::getUrl,urlKeyword);
+        }
+        return page(page,wrapper);
+    }
+}

+ 15 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsRoleMenuRelationServiceImpl.java

@@ -0,0 +1,15 @@
+package com.macro.mall.tiny.modules.ums.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.macro.mall.tiny.modules.ums.mapper.UmsRoleMenuRelationMapper;
+import com.macro.mall.tiny.modules.ums.model.UmsRoleMenuRelation;
+import com.macro.mall.tiny.modules.ums.service.UmsRoleMenuRelationService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 角色菜单关系管理Service实现类
+ * Created by macro on 2020/8/21.
+ */
+@Service
+public class UmsRoleMenuRelationServiceImpl extends ServiceImpl<UmsRoleMenuRelationMapper, UmsRoleMenuRelation> implements UmsRoleMenuRelationService {
+}

+ 15 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsRoleResourceRelationServiceImpl.java

@@ -0,0 +1,15 @@
+package com.macro.mall.tiny.modules.ums.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.macro.mall.tiny.modules.ums.mapper.UmsRoleResourceRelationMapper;
+import com.macro.mall.tiny.modules.ums.model.UmsRoleResourceRelation;
+import com.macro.mall.tiny.modules.ums.service.UmsRoleResourceRelationService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 角色资源关系管理Service实现类
+ * Created by macro on 2020/8/21.
+ */
+@Service
+public class UmsRoleResourceRelationServiceImpl extends ServiceImpl<UmsRoleResourceRelationMapper, UmsRoleResourceRelation> implements UmsRoleResourceRelationService {
+}

+ 116 - 0
src/main/java/com/macro/mall/tiny/modules/ums/service/impl/UmsRoleServiceImpl.java

@@ -0,0 +1,116 @@
+package com.macro.mall.tiny.modules.ums.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.macro.mall.tiny.modules.ums.mapper.UmsMenuMapper;
+import com.macro.mall.tiny.modules.ums.mapper.UmsResourceMapper;
+import com.macro.mall.tiny.modules.ums.mapper.UmsRoleMapper;
+import com.macro.mall.tiny.modules.ums.model.*;
+import com.macro.mall.tiny.modules.ums.service.UmsAdminCacheService;
+import com.macro.mall.tiny.modules.ums.service.UmsRoleMenuRelationService;
+import com.macro.mall.tiny.modules.ums.service.UmsRoleResourceRelationService;
+import com.macro.mall.tiny.modules.ums.service.UmsRoleService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 后台角色管理Service实现类
+ * Created by macro on 2018/9/30.
+ */
+@Service
+public class UmsRoleServiceImpl extends ServiceImpl<UmsRoleMapper,UmsRole>implements UmsRoleService {
+    @Autowired
+    private UmsAdminCacheService adminCacheService;
+    @Autowired
+    private UmsRoleMenuRelationService roleMenuRelationService;
+    @Autowired
+    private UmsRoleResourceRelationService roleResourceRelationService;
+    @Autowired
+    private UmsMenuMapper menuMapper;
+    @Autowired
+    private UmsResourceMapper resourceMapper;
+    @Override
+    public boolean create(UmsRole role) {
+        role.setCreateTime(new Date());
+        role.setAdminCount(0);
+        role.setSort(0);
+        return save(role);
+    }
+
+    @Override
+    public boolean delete(List<Long> ids) {
+        boolean success = removeByIds(ids);
+        adminCacheService.delResourceListByRoleIds(ids);
+        return success;
+    }
+
+    @Override
+    public Page<UmsRole> list(String keyword, Integer pageSize, Integer pageNum) {
+        Page<UmsRole> page = new Page<>(pageNum,pageSize);
+        QueryWrapper<UmsRole> wrapper = new QueryWrapper<>();
+        LambdaQueryWrapper<UmsRole> lambda = wrapper.lambda();
+        if(StrUtil.isNotEmpty(keyword)){
+            lambda.like(UmsRole::getName,keyword);
+        }
+        return page(page,wrapper);
+    }
+
+    @Override
+    public List<UmsMenu> getMenuList(Long adminId) {
+        return menuMapper.getMenuList(adminId);
+    }
+
+    @Override
+    public List<UmsMenu> listMenu(Long roleId) {
+        return menuMapper.getMenuListByRoleId(roleId);
+    }
+
+    @Override
+    public List<UmsResource> listResource(Long roleId) {
+        return resourceMapper.getResourceListByRoleId(roleId);
+    }
+
+    @Override
+    public int allocMenu(Long roleId, List<Long> menuIds) {
+        //先删除原有关系
+        QueryWrapper<UmsRoleMenuRelation> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(UmsRoleMenuRelation::getRoleId,roleId);
+        roleMenuRelationService.remove(wrapper);
+        //批量插入新关系
+        List<UmsRoleMenuRelation> relationList = new ArrayList<>();
+        for (Long menuId : menuIds) {
+            UmsRoleMenuRelation relation = new UmsRoleMenuRelation();
+            relation.setRoleId(roleId);
+            relation.setMenuId(menuId);
+            relationList.add(relation);
+        }
+        roleMenuRelationService.saveBatch(relationList);
+        return menuIds.size();
+    }
+
+    @Override
+    public int allocResource(Long roleId, List<Long> resourceIds) {
+        //先删除原有关系
+        QueryWrapper<UmsRoleResourceRelation> wrapper = new QueryWrapper<>();
+        wrapper.lambda().eq(UmsRoleResourceRelation::getRoleId,roleId);
+        roleResourceRelationService.remove(wrapper);
+        //批量插入新关系
+        List<UmsRoleResourceRelation> relationList = new ArrayList<>();
+        for (Long resourceId : resourceIds) {
+            UmsRoleResourceRelation relation = new UmsRoleResourceRelation();
+            relation.setRoleId(roleId);
+            relation.setResourceId(resourceId);
+            relationList.add(relation);
+        }
+        roleResourceRelationService.saveBatch(relationList);
+        adminCacheService.delResourceListByRole(roleId);
+        return resourceIds.size();
+    }
+}

+ 12 - 0
src/main/java/com/macro/mall/tiny/security/annotation/CacheException.java

@@ -0,0 +1,12 @@
+package com.macro.mall.tiny.security.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 自定义注解,有该注解的缓存方法会抛出异常
+ */
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface CacheException {
+}

+ 50 - 0
src/main/java/com/macro/mall/tiny/security/aspect/RedisCacheAspect.java

@@ -0,0 +1,50 @@
+package com.macro.mall.tiny.security.aspect;
+
+import com.macro.mall.tiny.security.annotation.CacheException;
+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.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+/**
+ * Redis缓存切面,防止Redis宕机影响正常业务逻辑
+ * Created by macro on 2020/3/17.
+ */
+@Aspect
+@Component
+@Order(2)
+public class RedisCacheAspect {
+    private static Logger LOGGER = LoggerFactory.getLogger(RedisCacheAspect.class);
+
+    @Pointcut("execution(public * com.macro.mall.tiny.service.*CacheService.*(..))")
+    public void cacheAspect() {
+    }
+
+    @Around("cacheAspect()")
+    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
+        Signature signature = joinPoint.getSignature();
+        MethodSignature methodSignature = (MethodSignature) signature;
+        Method method = methodSignature.getMethod();
+        Object result = null;
+        try {
+            result = joinPoint.proceed();
+        } catch (Throwable throwable) {
+            //有CacheException注解的方法需要抛出异常
+            if (method.isAnnotationPresent(CacheException.class)) {
+                throw throwable;
+            } else {
+                LOGGER.error(throwable.getMessage());
+            }
+        }
+        return result;
+    }
+
+}

+ 51 - 0
src/main/java/com/macro/mall/tiny/security/component/DynamicAccessDecisionManager.java

@@ -0,0 +1,51 @@
+package com.macro.mall.tiny.security.component;
+
+import cn.hutool.core.collection.CollUtil;
+import org.springframework.security.access.AccessDecisionManager;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.authentication.InsufficientAuthenticationException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * 动态权限决策管理器,用于判断用户是否有访问权限
+ * Created by macro on 2020/2/7.
+ */
+public class DynamicAccessDecisionManager implements AccessDecisionManager {
+
+    @Override
+    public void decide(Authentication authentication, Object object,
+                       Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
+        // 当接口未被配置资源时直接放行
+        if (CollUtil.isEmpty(configAttributes)) {
+            return;
+        }
+        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
+        while (iterator.hasNext()) {
+            ConfigAttribute configAttribute = iterator.next();
+            //将访问所需资源或用户拥有资源进行比对
+            String needAuthority = configAttribute.getAttribute();
+            for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
+                if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
+                    return;
+                }
+            }
+        }
+        throw new AccessDeniedException("抱歉,您没有访问权限");
+    }
+
+    @Override
+    public boolean supports(ConfigAttribute configAttribute) {
+        return true;
+    }
+
+    @Override
+    public boolean supports(Class<?> aClass) {
+        return true;
+    }
+
+}

+ 77 - 0
src/main/java/com/macro/mall/tiny/security/component/DynamicSecurityFilter.java

@@ -0,0 +1,77 @@
+package com.macro.mall.tiny.security.component;
+
+import com.macro.mall.tiny.security.config.IgnoreUrlsConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.access.SecurityMetadataSource;
+import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
+import org.springframework.security.access.intercept.InterceptorStatusToken;
+import org.springframework.security.web.FilterInvocation;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * 动态权限过滤器,用于实现基于路径的动态权限过滤
+ * Created by macro on 2020/2/7.
+ */
+public class DynamicSecurityFilter extends AbstractSecurityInterceptor implements Filter {
+
+    @Autowired
+    private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
+    @Autowired
+    private IgnoreUrlsConfig ignoreUrlsConfig;
+
+    @Autowired
+    public void setMyAccessDecisionManager(DynamicAccessDecisionManager dynamicAccessDecisionManager) {
+        super.setAccessDecisionManager(dynamicAccessDecisionManager);
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
+        //OPTIONS请求直接放行
+        if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){
+            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
+            return;
+        }
+        //白名单请求直接放行
+        PathMatcher pathMatcher = new AntPathMatcher();
+        for (String path : ignoreUrlsConfig.getUrls()) {
+            if(pathMatcher.match(path,request.getRequestURI())){
+                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
+                return;
+            }
+        }
+        //此处会调用AccessDecisionManager中的decide方法进行鉴权操作
+        InterceptorStatusToken token = super.beforeInvocation(fi);
+        try {
+            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
+        } finally {
+            super.afterInvocation(token, null);
+        }
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+    @Override
+    public Class<?> getSecureObjectClass() {
+        return FilterInvocation.class;
+    }
+
+    @Override
+    public SecurityMetadataSource obtainSecurityMetadataSource() {
+        return dynamicSecurityMetadataSource;
+    }
+
+}

+ 64 - 0
src/main/java/com/macro/mall/tiny/security/component/DynamicSecurityMetadataSource.java

@@ -0,0 +1,64 @@
+package com.macro.mall.tiny.security.component;
+
+import cn.hutool.core.util.URLUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.web.FilterInvocation;
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
+
+import javax.annotation.PostConstruct;
+import java.util.*;
+
+/**
+ * 动态权限数据源,用于获取动态权限规则
+ * Created by macro on 2020/2/7.
+ */
+public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
+
+    private static Map<String, ConfigAttribute> configAttributeMap = null;
+    @Autowired
+    private DynamicSecurityService dynamicSecurityService;
+
+    @PostConstruct
+    public void loadDataSource() {
+        configAttributeMap = dynamicSecurityService.loadDataSource();
+    }
+
+    public void clearDataSource() {
+        configAttributeMap.clear();
+        configAttributeMap = null;
+    }
+
+    @Override
+    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
+        if (configAttributeMap == null) this.loadDataSource();
+        List<ConfigAttribute>  configAttributes = new ArrayList<>();
+        //获取当前访问的路径
+        String url = ((FilterInvocation) o).getRequestUrl();
+        String path = URLUtil.getPath(url);
+        PathMatcher pathMatcher = new AntPathMatcher();
+        Iterator<String> iterator = configAttributeMap.keySet().iterator();
+        //获取访问该路径所需资源
+        while (iterator.hasNext()) {
+            String pattern = iterator.next();
+            if (pathMatcher.match(pattern, path)) {
+                configAttributes.add(configAttributeMap.get(pattern));
+            }
+        }
+        // 未设置操作请求权限,返回空集合
+        return configAttributes;
+    }
+
+    @Override
+    public Collection<ConfigAttribute> getAllConfigAttributes() {
+        return null;
+    }
+
+    @Override
+    public boolean supports(Class<?> aClass) {
+        return true;
+    }
+
+}

+ 16 - 0
src/main/java/com/macro/mall/tiny/security/component/DynamicSecurityService.java

@@ -0,0 +1,16 @@
+package com.macro.mall.tiny.security.component;
+
+import org.springframework.security.access.ConfigAttribute;
+
+import java.util.Map;
+
+/**
+ * 动态权限相关业务类
+ * Created by macro on 2020/2/7.
+ */
+public interface DynamicSecurityService {
+    /**
+     * 加载资源ANT通配符和资源对应MAP
+     */
+    Map<String, ConfigAttribute> loadDataSource();
+}

+ 57 - 0
src/main/java/com/macro/mall/tiny/security/component/JwtAuthenticationTokenFilter.java

@@ -0,0 +1,57 @@
+package com.macro.mall.tiny.security.component;
+
+import com.macro.mall.security.util.JwtTokenUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * JWT登录授权过滤器
+ * Created by macro on 2018/4/26.
+ */
+public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
+    private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
+    @Autowired
+    private UserDetailsService userDetailsService;
+    @Autowired
+    private JwtTokenUtil jwtTokenUtil;
+    @Value("${jwt.tokenHeader}")
+    private String tokenHeader;
+    @Value("${jwt.tokenHead}")
+    private String tokenHead;
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request,
+                                    HttpServletResponse response,
+                                    FilterChain chain) throws ServletException, IOException {
+        String authHeader = request.getHeader(this.tokenHeader);
+        if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
+            String authToken = authHeader.substring(this.tokenHead.length());// The part after "Bearer "
+            String username = jwtTokenUtil.getUserNameFromToken(authToken);
+            LOGGER.info("checking username:{}", username);
+            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
+                UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
+                if (jwtTokenUtil.validateToken(authToken, userDetails)) {
+                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+                    LOGGER.info("authenticated user:{}", username);
+                    SecurityContextHolder.getContext().setAuthentication(authentication);
+                }
+            }
+        }
+        chain.doFilter(request, response);
+    }
+}

+ 27 - 0
src/main/java/com/macro/mall/tiny/security/component/RestAuthenticationEntryPoint.java

@@ -0,0 +1,27 @@
+package com.macro.mall.tiny.security.component;
+
+import cn.hutool.json.JSONUtil;
+import com.macro.mall.tiny.common.api.CommonResult;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 自定义返回结果:未登录或登录过期
+ * Created by macro on 2018/5/14.
+ */
+public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
+    @Override
+    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
+        response.setHeader("Access-Control-Allow-Origin", "*");
+        response.setHeader("Cache-Control","no-cache");
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType("application/json");
+        response.getWriter().println(JSONUtil.parse(CommonResult.unauthorized(authException.getMessage())));
+        response.getWriter().flush();
+    }
+}

+ 29 - 0
src/main/java/com/macro/mall/tiny/security/component/RestfulAccessDeniedHandler.java

@@ -0,0 +1,29 @@
+package com.macro.mall.tiny.security.component;
+
+import cn.hutool.json.JSONUtil;
+import com.macro.mall.tiny.common.api.CommonResult;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.web.access.AccessDeniedHandler;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 自定义返回结果:没有权限访问时
+ * Created by macro on 2018/4/26.
+ */
+public class RestfulAccessDeniedHandler implements AccessDeniedHandler{
+    @Override
+    public void handle(HttpServletRequest request,
+                       HttpServletResponse response,
+                       AccessDeniedException e) throws IOException, ServletException {
+        response.setHeader("Access-Control-Allow-Origin", "*");
+        response.setHeader("Cache-Control","no-cache");
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType("application/json");
+        response.getWriter().println(JSONUtil.parse(CommonResult.forbidden(e.getMessage())));
+        response.getWriter().flush();
+    }
+}

+ 21 - 0
src/main/java/com/macro/mall/tiny/security/config/IgnoreUrlsConfig.java

@@ -0,0 +1,21 @@
+package com.macro.mall.tiny.security.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 用于配置白名单资源路径
+ * Created by macro on 2018/11/5.
+ */
+@Getter
+@Setter
+@ConfigurationProperties(prefix = "secure.ignored")
+public class IgnoreUrlsConfig {
+
+    private List<String> urls = new ArrayList<>();
+
+}

+ 127 - 0
src/main/java/com/macro/mall/tiny/security/config/SecurityConfig.java

@@ -0,0 +1,127 @@
+package com.macro.mall.tiny.security.config;
+
+import com.macro.mall.security.util.JwtTokenUtil;
+import com.macro.mall.tiny.security.component.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
+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.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+
+/**
+ * 对SpringSecurity的配置的扩展,支持自定义白名单资源路径和查询用户逻辑
+ * Created by macro on 2019/11/5.
+ */
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Autowired(required = false)
+    private DynamicSecurityService dynamicSecurityService;
+
+    @Override
+    protected void configure(HttpSecurity httpSecurity) throws Exception {
+        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
+                .authorizeRequests();
+        //不需要保护的资源路径允许访问
+        for (String url : ignoreUrlsConfig().getUrls()) {
+            registry.antMatchers(url).permitAll();
+        }
+        //允许跨域请求的OPTIONS请求
+        registry.antMatchers(HttpMethod.OPTIONS)
+                .permitAll();
+        // 任何请求需要身份认证
+        registry.and()
+                .authorizeRequests()
+                .anyRequest()
+                .authenticated()
+                // 关闭跨站请求防护及不使用session
+                .and()
+                .csrf()
+                .disable()
+                .sessionManagement()
+                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+                // 自定义权限拒绝处理类
+                .and()
+                .exceptionHandling()
+                .accessDeniedHandler(restfulAccessDeniedHandler())
+                .authenticationEntryPoint(restAuthenticationEntryPoint())
+                // 自定义权限拦截器JWT过滤器
+                .and()
+                .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
+        //有动态权限配置时添加动态权限校验过滤器
+        if(dynamicSecurityService!=null){
+            registry.and().addFilterBefore(dynamicSecurityFilter(), FilterSecurityInterceptor.class);
+        }
+    }
+
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+        auth.userDetailsService(userDetailsService())
+                .passwordEncoder(passwordEncoder());
+    }
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+
+    @Bean
+    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
+        return new JwtAuthenticationTokenFilter();
+    }
+
+    @Bean
+    @Override
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        return super.authenticationManagerBean();
+    }
+
+    @Bean
+    public RestfulAccessDeniedHandler restfulAccessDeniedHandler() {
+        return new RestfulAccessDeniedHandler();
+    }
+
+    @Bean
+    public RestAuthenticationEntryPoint restAuthenticationEntryPoint() {
+        return new RestAuthenticationEntryPoint();
+    }
+
+    @Bean
+    public IgnoreUrlsConfig ignoreUrlsConfig() {
+        return new IgnoreUrlsConfig();
+    }
+
+    @Bean
+    public JwtTokenUtil jwtTokenUtil() {
+        return new JwtTokenUtil();
+    }
+
+    @ConditionalOnBean(name = "dynamicSecurityService")
+    @Bean
+    public DynamicAccessDecisionManager dynamicAccessDecisionManager() {
+        return new DynamicAccessDecisionManager();
+    }
+
+
+    @ConditionalOnBean(name = "dynamicSecurityService")
+    @Bean
+    public DynamicSecurityFilter dynamicSecurityFilter() {
+        return new DynamicSecurityFilter();
+    }
+
+    @ConditionalOnBean(name = "dynamicSecurityService")
+    @Bean
+    public DynamicSecurityMetadataSource dynamicSecurityMetadataSource() {
+        return new DynamicSecurityMetadataSource();
+    }
+
+}

+ 170 - 0
src/main/java/com/macro/mall/tiny/security/util/JwtTokenUtil.java

@@ -0,0 +1,170 @@
+package com.macro.mall.security.util;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * JwtToken生成的工具类
+ * JWT token的格式:header.payload.signature
+ * header的格式(算法、token的类型):
+ * {"alg": "HS512","typ": "JWT"}
+ * payload的格式(用户名、创建时间、生成时间):
+ * {"sub":"wang","created":1489079981393,"exp":1489684781}
+ * signature的生成算法:
+ * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
+ * Created by macro on 2018/4/26.
+ */
+public class JwtTokenUtil {
+    private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
+    private static final String CLAIM_KEY_USERNAME = "sub";
+    private static final String CLAIM_KEY_CREATED = "created";
+    @Value("${jwt.secret}")
+    private String secret;
+    @Value("${jwt.expiration}")
+    private Long expiration;
+    @Value("${jwt.tokenHead}")
+    private String tokenHead;
+
+    /**
+     * 根据负责生成JWT的token
+     */
+    private String generateToken(Map<String, Object> claims) {
+        return Jwts.builder()
+                .setClaims(claims)
+                .setExpiration(generateExpirationDate())
+                .signWith(SignatureAlgorithm.HS512, secret)
+                .compact();
+    }
+
+    /**
+     * 从token中获取JWT中的负载
+     */
+    private Claims getClaimsFromToken(String token) {
+        Claims claims = null;
+        try {
+            claims = Jwts.parser()
+                    .setSigningKey(secret)
+                    .parseClaimsJws(token)
+                    .getBody();
+        } catch (Exception e) {
+            LOGGER.info("JWT格式验证失败:{}", token);
+        }
+        return claims;
+    }
+
+    /**
+     * 生成token的过期时间
+     */
+    private Date generateExpirationDate() {
+        return new Date(System.currentTimeMillis() + expiration * 1000);
+    }
+
+    /**
+     * 从token中获取登录用户名
+     */
+    public String getUserNameFromToken(String token) {
+        String username;
+        try {
+            Claims claims = getClaimsFromToken(token);
+            username = claims.getSubject();
+        } catch (Exception e) {
+            username = null;
+        }
+        return username;
+    }
+
+    /**
+     * 验证token是否还有效
+     *
+     * @param token       客户端传入的token
+     * @param userDetails 从数据库中查询出来的用户信息
+     */
+    public boolean validateToken(String token, UserDetails userDetails) {
+        String username = getUserNameFromToken(token);
+        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
+    }
+
+    /**
+     * 判断token是否已经失效
+     */
+    private boolean isTokenExpired(String token) {
+        Date expiredDate = getExpiredDateFromToken(token);
+        return expiredDate.before(new Date());
+    }
+
+    /**
+     * 从token中获取过期时间
+     */
+    private Date getExpiredDateFromToken(String token) {
+        Claims claims = getClaimsFromToken(token);
+        return claims.getExpiration();
+    }
+
+    /**
+     * 根据用户信息生成token
+     */
+    public String generateToken(UserDetails userDetails) {
+        Map<String, Object> claims = new HashMap<>();
+        claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
+        claims.put(CLAIM_KEY_CREATED, new Date());
+        return generateToken(claims);
+    }
+
+    /**
+     * 当原来的token没过期时是可以刷新的
+     *
+     * @param oldToken 带tokenHead的token
+     */
+    public String refreshHeadToken(String oldToken) {
+        if(StrUtil.isEmpty(oldToken)){
+            return null;
+        }
+        String token = oldToken.substring(tokenHead.length());
+        if(StrUtil.isEmpty(token)){
+            return null;
+        }
+        //token校验不通过
+        Claims claims = getClaimsFromToken(token);
+        if(claims==null){
+            return null;
+        }
+        //如果token已经过期,不支持刷新
+        if(isTokenExpired(token)){
+            return null;
+        }
+        //如果token在30分钟之内刚刷新过,返回原token
+        if(tokenRefreshJustBefore(token,30*60)){
+            return token;
+        }else{
+            claims.put(CLAIM_KEY_CREATED, new Date());
+            return generateToken(claims);
+        }
+    }
+
+    /**
+     * 判断token在指定时间内是否刚刚刷新过
+     * @param token 原token
+     * @param time 指定时间(秒)
+     */
+    private boolean tokenRefreshJustBefore(String token, int time) {
+        Claims claims = getClaimsFromToken(token);
+        Date created = claims.get(CLAIM_KEY_CREATED, Date.class);
+        Date refreshDate = new Date();
+        //刷新时间在创建时间的指定时间内
+        if(refreshDate.after(created)&&refreshDate.before(DateUtil.offsetSecond(created,time))){
+            return true;
+        }
+        return false;
+    }
+}

+ 44 - 0
src/main/java/com/macro/mall/tiny/security/util/SpringUtil.java

@@ -0,0 +1,44 @@
+package com.macro.mall.security.util;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * Spring工具类
+ * Created by macro on 2020/3/3.
+ */
+@Component
+public class SpringUtil implements ApplicationContextAware {
+
+    private static ApplicationContext applicationContext;
+
+    // 获取applicationContext
+    public static ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        if (SpringUtil.applicationContext == null) {
+            SpringUtil.applicationContext = applicationContext;
+        }
+    }
+
+    // 通过name获取Bean
+    public static Object getBean(String name) {
+        return getApplicationContext().getBean(name);
+    }
+
+    // 通过class获取Bean
+    public static <T> T getBean(Class<T> clazz) {
+        return getApplicationContext().getBean(clazz);
+    }
+
+    // 通过name,以及Clazz返回指定的Bean
+    public static <T> T getBean(String name, Class<T> clazz) {
+        return getApplicationContext().getBean(name, clazz);
+    }
+
+}

+ 16 - 0
src/main/resources/application-dev.yml

@@ -0,0 +1,16 @@
+spring:
+  datasource:
+    url: jdbc:mysql://localhost:3306/power_system?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
+    username: root
+    password: abc45628
+  redis:
+    host: localhost # Redis服务器地址
+    database: 0 # Redis数据库索引(默认为0)
+    port: 6379 # Redis服务器连接端口
+    password: # Redis服务器连接密码(默认为空)
+    timeout: 3000ms # 连接超时时间(毫秒)
+
+logging:
+  level:
+    root: info
+    com.macro.mall: debug

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