├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── db ├── Dockerfile └── tumo_boot.sql ├── docker-compose.yml ├── generate ├── EasyCodeConfig.json └── README.md ├── imgs ├── image-20211019010331915.png ├── image-20211019010617453.png ├── image-20211019010644809.png ├── image-20211019010703801.png ├── image-20211019010720167.png └── image-20211019010743832.png ├── pom.xml ├── script ├── assembly │ ├── assembly.xml │ └── bin │ │ ├── start.sh │ │ └── stop.sh └── docker-compose.yml └── src ├── main ├── java │ └── cn │ │ └── tycoding │ │ └── boot │ │ ├── TumoBootApp.java │ │ ├── common │ │ ├── auth │ │ │ ├── AuthAutoConfiguration.java │ │ │ ├── filter │ │ │ │ └── CaptchaFilter.java │ │ │ ├── props │ │ │ │ └── AuthProperties.java │ │ │ └── utils │ │ │ │ ├── AuthService.java │ │ │ │ └── AuthUtil.java │ │ ├── core │ │ │ ├── api │ │ │ │ ├── HttpCode.java │ │ │ │ ├── QueryPage.java │ │ │ │ └── R.java │ │ │ ├── config │ │ │ │ └── JacksonConfiguration.java │ │ │ ├── constant │ │ │ │ ├── ApiConstant.java │ │ │ │ ├── AuthConstant.java │ │ │ │ ├── CacheConstant.java │ │ │ │ ├── CaptchaConstant.java │ │ │ │ └── CommonConstant.java │ │ │ ├── launch │ │ │ │ └── StartEventListener.java │ │ │ └── utils │ │ │ │ ├── BeanUtil.java │ │ │ │ ├── IpUtil.java │ │ │ │ ├── Is.java │ │ │ │ └── ServletUtil.java │ │ ├── log │ │ │ ├── LogAutoConfiguration.java │ │ │ ├── annotation │ │ │ │ └── ApiLog.java │ │ │ ├── aspect │ │ │ │ ├── ApiLogAspect.java │ │ │ │ └── RequestLogAspect.java │ │ │ ├── event │ │ │ │ ├── LogEvent.java │ │ │ │ └── LogListener.java │ │ │ ├── exception │ │ │ │ ├── GlobalExceptionTranslator.java │ │ │ │ ├── RestExceptionTranslator.java │ │ │ │ └── ServiceException.java │ │ │ ├── props │ │ │ │ └── LogProperties.java │ │ │ └── utils │ │ │ │ ├── SpringContextHolder.java │ │ │ │ └── SysLogUtil.java │ │ ├── mybatis │ │ │ ├── MybatisAutoConfiguration.java │ │ │ ├── config │ │ │ │ └── MybatisPlusConfig.java │ │ │ ├── constant │ │ │ │ └── MybatisConstant.java │ │ │ ├── intercept │ │ │ │ └── SqlLogInterceptor.java │ │ │ ├── props │ │ │ │ └── MybatisProperties.java │ │ │ └── utils │ │ │ │ └── MybatisUtil.java │ │ ├── oss │ │ │ ├── OssAutoConfiguration.java │ │ │ ├── props │ │ │ │ └── LocalFileProperties.java │ │ │ └── utils │ │ │ │ └── OssUtil.java │ │ └── redis │ │ │ ├── component │ │ │ └── RedisComponent.java │ │ │ └── utils │ │ │ ├── RedisUtil.java │ │ │ └── TokenInfo.java │ │ └── modules │ │ ├── auth │ │ ├── component │ │ │ ├── ResourceAuthExceptionEntryPoint.java │ │ │ ├── TumoAuth2ExceptionSerializer.java │ │ │ └── TumoWebResponseExceptionTranslator.java │ │ ├── config │ │ │ ├── AuthorizationServerConfig.java │ │ │ ├── ResourceServerConfig.java │ │ │ └── WebSecurityConfig.java │ │ ├── dto │ │ │ ├── TumoUser.java │ │ │ └── UserInfo.java │ │ ├── endpoint │ │ │ └── AuthTokenEndpoint.java │ │ ├── exception │ │ │ └── TumoAuth2Exception.java │ │ └── service │ │ │ └── UserDetailsServiceImpl.java │ │ ├── system │ │ ├── controller │ │ │ ├── OssFileController.java │ │ │ ├── SysDictController.java │ │ │ ├── SysDictItemController.java │ │ │ └── SysLogController.java │ │ ├── endpoint │ │ │ └── OssEndpoint.java │ │ ├── entity │ │ │ ├── OssFile.java │ │ │ ├── SysDict.java │ │ │ ├── SysDictItem.java │ │ │ └── SysLog.java │ │ ├── mapper │ │ │ ├── OssFileMapper.java │ │ │ ├── SysDictItemMapper.java │ │ │ ├── SysDictMapper.java │ │ │ └── SysLogMapper.java │ │ └── service │ │ │ ├── OssFileService.java │ │ │ ├── OssService.java │ │ │ ├── SysDictItemService.java │ │ │ ├── SysDictService.java │ │ │ ├── SysLogService.java │ │ │ └── impl │ │ │ ├── OssFileServiceImpl.java │ │ │ ├── OssServiceImpl.java │ │ │ ├── SysDictItemServiceImpl.java │ │ │ ├── SysDictServiceImpl.java │ │ │ └── SysLogServiceImpl.java │ │ └── upms │ │ ├── controller │ │ ├── SysDeptController.java │ │ ├── SysMenuController.java │ │ ├── SysRoleController.java │ │ └── SysUserController.java │ │ ├── dto │ │ ├── MenuMeta.java │ │ ├── MenuTree.java │ │ ├── MenuTreeUtil.java │ │ ├── SysRoleDTO.java │ │ └── SysUserDTO.java │ │ ├── entity │ │ ├── SysDept.java │ │ ├── SysMenu.java │ │ ├── SysRole.java │ │ ├── SysRoleMenu.java │ │ ├── SysUser.java │ │ └── SysUserRole.java │ │ ├── mapper │ │ ├── SysDeptMapper.java │ │ ├── SysMenuMapper.java │ │ ├── SysRoleMapper.java │ │ ├── SysRoleMenuMapper.java │ │ ├── SysUserMapper.java │ │ └── SysUserRoleMapper.java │ │ └── service │ │ ├── SysDeptService.java │ │ ├── SysMenuService.java │ │ ├── SysRoleMenuService.java │ │ ├── SysRoleService.java │ │ ├── SysUserRoleService.java │ │ ├── SysUserService.java │ │ └── impl │ │ ├── SysDeptServiceImpl.java │ │ ├── SysMenuServiceImpl.java │ │ ├── SysRoleMenuServiceImpl.java │ │ ├── SysRoleServiceImpl.java │ │ ├── SysUserRoleServiceImpl.java │ │ └── SysUserServiceImpl.java └── resources │ ├── META-INF │ └── spring.factories │ ├── application-dev.yml │ ├── application-prod.yml │ ├── application.yml │ ├── banner.txt │ ├── logback-spring.xml │ ├── mapper │ └── upms │ │ ├── SysMenuMapper.xml │ │ ├── SysUserMapper.xml │ │ └── SysUserRoleMapper.xml │ └── static │ └── default.png └── test └── java └── cn └── tycoding └── boot └── TumoTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | ### gradle ### 2 | .gradle 3 | /build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | 6 | ### STS ### 7 | .settings/ 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | 15 | ### IntelliJ IDEA ### 16 | .idea 17 | *.iws 18 | *.iml 19 | *.ipr 20 | rebel.xml 21 | 22 | ### NetBeans ### 23 | nbproject/private/ 24 | build/ 25 | nbbuild/ 26 | nbdist/ 27 | .nb-gradle/ 28 | 29 | ### maven ### 30 | target/ 31 | *.war 32 | *.ear 33 | *.zip 34 | *.tar 35 | *.tar.gz 36 | 37 | ### logs #### 38 | /logs/ 39 | *.sysLog 40 | 41 | ### temp ignore ### 42 | *.cache 43 | *.diff 44 | *.patch 45 | *.tmp 46 | *.java~ 47 | *.properties~ 48 | *.xml~ 49 | 50 | ### system ignore ### 51 | .DS_Store 52 | Thumbs.db 53 | Servers 54 | .metadata 55 | gen_code 56 | dump.rdb 57 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre 2 | MAINTAINER tycoding@sina.com 3 | 4 | WORKDIR /build 5 | 6 | ADD ./target/tumo-boot.jar ./app.jar 7 | 8 | EXPOSE 8010 9 | 10 | ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] 11 | 12 | CMD ["--spring.profiles.active=prod"] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-present TyCoding 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | 6 | Tumo Team —— Tumo-Boot 7 | 8 |

9 | 10 | ## 项目介绍 11 | 12 | **[Tumo-Boot](https://github.com/Tumo-Team/tumo-boot)** 是基于SpringBoot2.x、SpringSecurity的RBAC项目脚手架,前端基于Node、Vue3.x、Ant-Design-Vue2.x、Vite、TypeScript。 13 | 14 | 如果你已经熟悉了RBAC单体项目开发,你可以学习 **Tumo-Cloud:** [https://github.com/Tumo-Team/tumo-cloud](https://github.com/Tumo-Team/tumo-cloud)。 15 | 16 | ## 项目地址 17 | 18 | - Tumo-Boot项目预览:[http://boot.tycoding.cn](http://boot.tycoding.cn) 19 | - Tumo-Boot文档预览:[http://docs.boot.tycoding.cn](http://docs.boot.tycoding.cn) 20 | - Tumo-Boot后端源码:[https://github.com/Tumo-Team/tumo-boot](https://github.com/Tumo-Team/tumo-boot) 21 | - Tumo-Boot前端源码:[https://github.com/Tumo-Team/tumo-boot-ui](https://github.com/Tumo-Team/tumo-boot-ui) 22 | - Tumo-Boot文档源码:[https://github.com/Tumo-Team/tumo-boot-docs](https://github.com/Tumo-Team/tumo-boot-docs) 23 | 24 | ## 技术栈 25 | 26 | **环境** 27 | 28 | | Name | Version | 29 | | ----- |-----------| 30 | | JDK | 1.8 | 31 | | MySql | 8.x | 32 | | OS | MacOS13.x | 33 | | IDEA | 2022.x | 34 | 35 | **后端** 36 | 37 | | Name | Version | Document | 38 | | --------------- |---------| ------------------------------------------------------------ | 39 | | Spring Boot | 2.x | [https://github.com/spring-projects/spring-boot](https://github.com/spring-projects/spring-boot) | 40 | | Mybatis-Plus | 3.x | [https://baomidou.com/guide/](https://baomidou.com/guide/) | 41 | | Hutool | 5.x | [https://hutool.cn/docs/#/](https://hutool.cn/docs/#/) | 42 | 43 | **前端** 44 | 45 | | Name | Version | Document | 46 | | -------------- |-------| ------------------------------------------------------------ | 47 | | Vben | 2.x | [https://github.com/anncwb/vue-vben-admin](https://github.com/anncwb/vue-vben-admin) | 48 | | Vue.js | 3.x | [https://cn.vuejs.org/v2/guide/](https://cn.vuejs.org/v2/guide/) | 49 | | Ant-Design-Vue | 2.x | [https://www.antdv.com/docs/vue/introduce-cn/](https://www.antdv.com/docs/vue/introduce-cn/) | 50 | 51 | ## 运行项目 52 | 53 | 详细的文档请看:[http://docs.boot.tycoding.cn](http://docs.boot.tycoding.cn) 54 | 55 | ## 图片预览 56 | 57 | 58 | ![image-20211019010331915](imgs/image-20211019010331915.png) 59 | 60 | ![image-20211019010617453](imgs/image-20211019010617453.png) 61 | 62 | ![image-20211019010644809](imgs/image-20211019010644809.png) 63 | 64 | ![image-20211019010703801](imgs/image-20211019010703801.png) 65 | 66 | ![image-20211019010720167](imgs/image-20211019010720167.png) 67 | 68 | ![image-20211019010743832](imgs/image-20211019010743832.png) 69 | 70 | # 联系 71 | 72 | - 个人博客:[http://tycoding.cn](http://tycoding.cn/) 73 | - GitHub:https://github.com/tycoding 74 | - 微信公众号:程序员涂陌 75 | - QQ交流群:866685601 76 | 77 | ## License 78 | 79 | [MIT](https://github.com/Tumo-Team/tumo-boot/blob/master/LICENSE) 80 | 81 | Copyright (c) 2021-present TyCoding 82 | 83 | -------------------------------------------------------------------------------- /db/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mysql:5.7.34 2 | 3 | ENV TZ=Asia/Shanghai 4 | 5 | RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 6 | 7 | COPY ./tumo_boot.sql /docker-entrypoint-initdb.d 8 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | services: 4 | tumo-boot-mysql: 5 | image: tumo-boot-mysql 6 | build: 7 | context: ./db 8 | environment: 9 | MYSQL_ROOT_PASSWORD: root 10 | restart: always 11 | container_name: tumo-boot-mysql 12 | ports: 13 | - 3306:3306 14 | 15 | tumo-boot-redis: 16 | image: redis:6.0 17 | restart: always 18 | hostname: tumo-boot-redis 19 | container_name: tumo-boot-redis 20 | privileged: true 21 | command: "redis-server --appendonly yes" 22 | environment: 23 | - TZ=Asia/Shanghai 24 | ports: 25 | - 6379:6379 26 | networks: 27 | - tumo_boot_net 28 | 29 | tumo-boot: 30 | image: tumo-boot 31 | build: 32 | context: ./ 33 | restart: always 34 | container_name: tumo-boot 35 | hostname: tumo-boot 36 | ports: 37 | - 8090:8090 38 | environment: 39 | - TZ=Asia/Shanghai 40 | networks: 41 | tumo_boot_net: 42 | ipv4_address: 172.30.0.80 43 | 44 | networks: 45 | tumo_boot_net: 46 | driver: bridge 47 | ipam: 48 | config: 49 | - subnet: 172.30.0.0/16 50 | -------------------------------------------------------------------------------- /generate/README.md: -------------------------------------------------------------------------------- 1 | # 代码生成 2 | 3 | -------------------------------------------------------------------------------- /imgs/image-20211019010331915.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tumo-Team/tumo-boot/f110e7741ae70c2a9c21e368965aa0acfacf39cd/imgs/image-20211019010331915.png -------------------------------------------------------------------------------- /imgs/image-20211019010617453.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tumo-Team/tumo-boot/f110e7741ae70c2a9c21e368965aa0acfacf39cd/imgs/image-20211019010617453.png -------------------------------------------------------------------------------- /imgs/image-20211019010644809.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tumo-Team/tumo-boot/f110e7741ae70c2a9c21e368965aa0acfacf39cd/imgs/image-20211019010644809.png -------------------------------------------------------------------------------- /imgs/image-20211019010703801.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tumo-Team/tumo-boot/f110e7741ae70c2a9c21e368965aa0acfacf39cd/imgs/image-20211019010703801.png -------------------------------------------------------------------------------- /imgs/image-20211019010720167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tumo-Team/tumo-boot/f110e7741ae70c2a9c21e368965aa0acfacf39cd/imgs/image-20211019010720167.png -------------------------------------------------------------------------------- /imgs/image-20211019010743832.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tumo-Team/tumo-boot/f110e7741ae70c2a9c21e368965aa0acfacf39cd/imgs/image-20211019010743832.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.7 9 | 10 | 11 | 12 | tumo-boot 13 | cn.tycoding.boot 14 | tumo-boot 15 | jar 16 | http://docs.boot.tycoding.cn 17 | 18 | 19 | 1.8 20 | 2.6.8 21 | 1.2.15 22 | 3.5.3.1 23 | 3.12.0 24 | 5.8.11 25 | 1.5.4 26 | 27 | 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-web 33 | 34 | 35 | 36 | spring-boot-starter-tomcat 37 | org.springframework.boot 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-undertow 45 | 46 | 47 | 48 | 49 | mysql 50 | mysql-connector-java 51 | runtime 52 | 53 | 54 | 55 | com.baomidou 56 | mybatis-plus-boot-starter 57 | ${mybatis-plus.version} 58 | 59 | 60 | com.zaxxer 61 | HikariCP 62 | 63 | 64 | 65 | 66 | 67 | com.alibaba 68 | druid-spring-boot-starter 69 | ${druid.version} 70 | 71 | 72 | 73 | 74 | org.springframework.security.oauth.boot 75 | spring-security-oauth2-autoconfigure 76 | ${security-oauth2-autoconfigure.version} 77 | 78 | 79 | org.springframework.boot 80 | spring-boot-starter-security 81 | 82 | 83 | 84 | org.springframework.boot 85 | spring-boot-starter-aop 86 | 87 | 88 | 89 | 90 | org.springframework.boot 91 | spring-boot-starter-data-redis 92 | 93 | 94 | 95 | 96 | cn.hutool 97 | hutool-all 98 | ${hutool.version} 99 | 100 | 101 | 102 | 103 | ma.glasnost.orika 104 | orika-core 105 | ${orika.version} 106 | 107 | 108 | 109 | 110 | org.apache.commons 111 | commons-lang3 112 | ${commons-lang3.version} 113 | 114 | 115 | 116 | 117 | org.projectlombok 118 | lombok 119 | true 120 | 121 | 122 | 123 | 124 | org.springframework.boot 125 | spring-boot-configuration-processor 126 | true 127 | 128 | 129 | 130 | 131 | org.springframework.boot 132 | spring-boot-devtools 133 | runtime 134 | true 135 | 136 | 137 | 138 | org.springframework.boot 139 | spring-boot-starter-test 140 | test 141 | 142 | 143 | junit 144 | junit 145 | test 146 | 147 | 148 | 149 | 150 | ${project.name} 151 | 152 | 153 | org.springframework.boot 154 | spring-boot-maven-plugin 155 | 156 | ${project.build.finalName} 157 | 158 | true 159 | 160 | 161 | 162 | 163 | 164 | repackage 165 | 166 | 167 | 168 | 169 | 170 | org.apache.maven.plugins 171 | maven-assembly-plugin 172 | 3.3.0 173 | 174 | 175 | 176 | cn.tycoding.boot.TumoBootApp 177 | 178 | 179 | 180 | /script/assembly/assembly.xml 181 | 182 | 183 | 184 | 185 | make-assembly 186 | package 187 | 188 | single 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /script/assembly/assembly.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | bin 8 | 9 | 10 | tar.gz 11 | 12 | 13 | true 14 | 15 | 16 | 20 | 21 | 22 | script/assembly/bin 23 | bin 24 | 0755 25 | 26 | unix 27 | true 28 | 29 | 30 | 31 | script/assembly/config 32 | config 33 | 0644 34 | 35 | 36 | 37 | src/main/resources 38 | /config 39 | 40 | **/*.properties 41 | **/*.yml 42 | banner.txt 43 | 44 | true 45 | 46 | 47 | 48 | target 49 | lib 50 | 51 | *.jar 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /script/assembly/bin/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 项目名称 4 | SERVER_NAME="${project.artifactId}" 5 | 6 | # jar名称 7 | JAR_NAME="${project.build.finalName}.jar" 8 | 9 | # 进入bin目录 10 | cd `dirname $0` 11 | # bin目录绝对路径 12 | BIN_DIR=`pwd` 13 | # 返回到上一级项目根目录路径 14 | cd .. 15 | # 打印项目根目录绝对路径 16 | # `pwd` 执行系统命令并获得结果 17 | DEPLOY_DIR=`pwd` 18 | 19 | # 外部配置文件绝对目录,如果是目录需要/结尾,也可以直接指定文件 20 | # 如果指定的是目录,spring则会读取目录中的所有配置文件 21 | CONF_DIR=$DEPLOY_DIR/config 22 | # SERVER_PORT=`sed '/server.port/!d;s/.*=//' config/application.properties | tr -d '\r'` 23 | # 获取应用的端口号 24 | SERVER_PORT=`sed -nr '/port: [0-9]+/ s/.*port: +([0-9]+).*/\1/p' config/application.yml` 25 | 26 | PIDS=`ps -f | grep java | grep "$CONF_DIR" |awk '{print $2}'` 27 | if [ "$1" = "status" ]; then 28 | if [ -n "$PIDS" ]; then 29 | echo "The $SERVER_NAME is running...!" 30 | echo "PID: $PIDS" 31 | exit 0 32 | else 33 | echo "The $SERVER_NAME is stopped" 34 | exit 0 35 | fi 36 | fi 37 | 38 | if [ -n "$PIDS" ]; then 39 | echo "ERROR: The $SERVER_NAME already started!" 40 | echo "PID: $PIDS" 41 | exit 1 42 | fi 43 | 44 | if [ -n "$SERVER_PORT" ]; then 45 | SERVER_PORT_COUNT=`netstat -tln | grep $SERVER_PORT | wc -l` 46 | if [ $SERVER_PORT_COUNT -gt 0 ]; then 47 | echo "ERROR: The $SERVER_NAME port $SERVER_PORT already used!" 48 | exit 1 49 | fi 50 | fi 51 | 52 | # 项目日志输出绝对路径 53 | LOGS_DIR=$DEPLOY_DIR/logs 54 | # 如果logs文件夹不存在,则创建文件夹 55 | if [ ! -d $LOGS_DIR ]; then 56 | mkdir $LOGS_DIR 57 | fi 58 | STDOUT_FILE=$LOGS_DIR/catalina.log 59 | 60 | # JVM Configuration 61 | JAVA_OPTS=" -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true " 62 | JAVA_DEBUG_OPTS="" 63 | if [ "$1" = "debug" ]; then 64 | JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n " 65 | fi 66 | 67 | JAVA_JMX_OPTS="" 68 | if [ "$1" = "jmx" ]; then 69 | JAVA_JMX_OPTS=" -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false " 70 | fi 71 | 72 | JAVA_MEM_OPTS="" 73 | BITS=`java -version 2>&1 | grep -i 64-bit` 74 | if [ -n "$BITS" ]; then 75 | JAVA_MEM_OPTS=" -server -Xmx512m -Xms512m -Xmn256m -Xss256k -XX:+DisableExplicitGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 " 76 | else 77 | JAVA_MEM_OPTS=" -server -Xms512m -Xmx512m -XX:SurvivorRatio=2 -XX:+UseParallelGC " 78 | fi 79 | 80 | # 加载外部log4j2文件的配置 81 | LOG_IMPL_FILE=log4j2.xml 82 | LOGGING_CONFIG="" 83 | if [ -f "$CONF_DIR/$LOG_IMPL_FILE" ] 84 | then 85 | LOGGING_CONFIG="-Dlogging.config=$CONF_DIR/$LOG_IMPL_FILE" 86 | fi 87 | CONFIG_FILES=" -Dlogging.path=$LOGS_DIR $LOGGING_CONFIG -Dspring.config.location=$CONF_DIR/ " 88 | echo -e "Starting the $SERVER_NAME ..." 89 | nohup java $JAVA_OPTS $JAVA_MEM_OPTS $JAVA_DEBUG_OPTS $JAVA_JMX_OPTS $CONFIG_FILES -jar $DEPLOY_DIR/lib/$JAR_NAME > $STDOUT_FILE 2>&1 & 90 | 91 | COUNT=0 92 | while [ $COUNT -lt 1 ]; do 93 | echo -e ".\c" 94 | sleep 1 95 | if [ -n "$SERVER_PORT" ]; then 96 | COUNT=`netstat -an | grep $SERVER_PORT | wc -l` 97 | else 98 | COUNT=`ps -f | grep java | grep "$DEPLOY_DIR" | awk '{print $2}' | wc -l` 99 | fi 100 | if [ $COUNT -gt 0 ]; then 101 | break 102 | fi 103 | done 104 | 105 | 106 | echo "OK!" 107 | PIDS=`ps -f | grep java | grep "$DEPLOY_DIR" | awk '{print $2}'` 108 | echo "PID: $PIDS" 109 | echo "STDOUT: $STDOUT_FILE" 110 | -------------------------------------------------------------------------------- /script/assembly/bin/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 项目名称 4 | APPLICATION="${project.artifactId}" 5 | 6 | # 项目启动jar包名称 7 | APPLICATION_JAR="${project.build.finalName}.jar" 8 | 9 | # 通过项目名称查找到PI,然后kill -9 pid 10 | PID=$(ps -ef | grep "${APPLICATION_JAR}" | grep -v grep | awk '{ print $2 }') 11 | if [[ -z "$PID" ]] 12 | then 13 | echo ${APPLICATION} is already stopped 14 | else 15 | echo kill ${PID} 16 | kill -9 ${PID} 17 | echo ${APPLICATION} stopped successfully 18 | fi -------------------------------------------------------------------------------- /script/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | services: 4 | tumo-boot-mysql: 5 | image: registry.cn-hangzhou.aliyuncs.com/tumo/tumo-boot-mysql 6 | environment: 7 | MYSQL_ROOT_PASSWORD: root 8 | restart: always 9 | container_name: tumo-boot-mysql 10 | volumes: 11 | - ./mysql_data:/var/lib/mysql 12 | ports: 13 | - 3306:3306 14 | networks: 15 | - tumo_boot_net 16 | 17 | tumo-boot-redis: 18 | image: redis:6.0 19 | restart: always 20 | hostname: tumo-boot-redis 21 | container_name: tumo-boot-redis 22 | privileged: true 23 | command: "redis-server --appendonly yes" 24 | volumes: 25 | - ./redis_data:/data 26 | environment: 27 | - TZ=Asia/Shanghai 28 | ports: 29 | - 6379:6379 30 | networks: 31 | - tumo_boot_net 32 | 33 | tumo-boot: 34 | image: registry.cn-hangzhou.aliyuncs.com/tumo/tumo-boot 35 | restart: always 36 | container_name: tumo-boot 37 | hostname: tumo-boot 38 | volumes: 39 | - ./logs:/build/logs 40 | ports: 41 | - 8090:8090 42 | environment: 43 | - TZ=Asia/Shanghai 44 | networks: 45 | tumo_boot_net: 46 | ipv4_address: 172.30.0.80 47 | 48 | tumo-boot-ui: 49 | image: registry.cn-hangzhou.aliyuncs.com/tumo/tumo-boot-ui 50 | restart: always 51 | container_name: tumo-boot-ui 52 | ports: 53 | - 8091:80 54 | networks: 55 | - tumo_boot_net 56 | 57 | networks: 58 | tumo_boot_net: 59 | driver: bridge 60 | ipam: 61 | config: 62 | - subnet: 172.30.0.0/16 63 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/TumoBootApp.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * SpringBoot启动器类 8 | * 9 | * @author tycoding 10 | * @since 2021/5/21 11 | */ 12 | @SpringBootApplication 13 | public class TumoBootApp { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(TumoBootApp.class, args); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/auth/AuthAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.auth; 2 | 3 | import cn.tycoding.boot.common.auth.props.AuthProperties; 4 | import cn.tycoding.boot.common.auth.utils.AuthService; 5 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | /** 10 | * Auth 配置注入 11 | * 12 | * @author tycoding 13 | * @since 2021/5/21 14 | */ 15 | @Configuration 16 | @EnableConfigurationProperties({AuthProperties.class}) 17 | public class AuthAutoConfiguration { 18 | 19 | @Bean("auth") 20 | public AuthService authService(AuthProperties authProperties) { 21 | return new AuthService(authProperties); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/auth/filter/CaptchaFilter.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.auth.filter; 2 | 3 | import cn.tycoding.boot.common.core.api.HttpCode; 4 | import cn.tycoding.boot.common.core.constant.ApiConstant; 5 | import cn.tycoding.boot.common.auth.utils.AuthUtil; 6 | import cn.tycoding.boot.common.core.api.R; 7 | import cn.tycoding.boot.common.core.constant.CacheConstant; 8 | import cn.tycoding.boot.common.core.utils.ServletUtil; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.data.redis.core.RedisTemplate; 12 | import org.springframework.stereotype.Component; 13 | import org.springframework.web.bind.ServletRequestUtils; 14 | import org.springframework.web.filter.OncePerRequestFilter; 15 | 16 | import javax.servlet.FilterChain; 17 | import javax.servlet.ServletException; 18 | import javax.servlet.http.HttpServletRequest; 19 | import javax.servlet.http.HttpServletResponse; 20 | import java.io.IOException; 21 | 22 | /** 23 | * 验证码过滤器 24 | * 25 | * @author tycoding 26 | * @since 2021/5/21 27 | */ 28 | @Slf4j 29 | @Component 30 | @RequiredArgsConstructor 31 | public class CaptchaFilter extends OncePerRequestFilter { 32 | 33 | private final RedisTemplate redisTemplate; 34 | 35 | @Override 36 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { 37 | if (ApiConstant.API_OAUTH_TOKEN.equals(request.getRequestURI())) { 38 | String headerKey = request.getHeader(AuthUtil.CAPTCHA_HEADER_KEY); 39 | if (headerKey == null) { 40 | // 特殊处理,对于类似Swagger中直接获取认证Token时不带验证码 41 | log.info("正在进行请求授权,未携带Captcha-Key请求头,不进行验证码校验"); 42 | chain.doFilter(request, response); 43 | return; 44 | } 45 | 46 | String code = ServletRequestUtils.getStringParameter(request, AuthUtil.CAPTCHA_FORM_KEY); 47 | String redisCode = (String) redisTemplate.opsForValue().get(CacheConstant.CAPTCHA_PREFIX + headerKey); 48 | if (code == null || !code.toLowerCase().equals(redisCode)) { 49 | ServletUtil.write(response, new R<>(HttpCode.FAILURE.getCode(), AuthUtil.CAPTCHA_ERROR_INFO)); 50 | return; 51 | } 52 | } 53 | chain.doFilter(request, response); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/auth/props/AuthProperties.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.auth.props; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * 自定义Auth模块 YML配置映射实体 11 | * 12 | * @author tycoding 13 | * @since 2021/5/21 14 | */ 15 | @Data 16 | @ConfigurationProperties("tumo-boot.auth") 17 | public class AuthProperties { 18 | 19 | /** 20 | * 默认忽略拦截的URL集合 21 | */ 22 | private List skipUrl = new ArrayList(); 23 | 24 | /** 25 | * 是否开启演示环境 26 | */ 27 | private Boolean isDemoEnv; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/auth/utils/AuthService.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.auth.utils; 2 | 3 | import cn.tycoding.boot.common.auth.props.AuthProperties; 4 | import lombok.AllArgsConstructor; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.GrantedAuthority; 7 | import org.springframework.util.PatternMatchUtils; 8 | import org.springframework.util.StringUtils; 9 | 10 | import java.util.Collection; 11 | 12 | /** 13 | * 接口权限校验 14 | * 15 | * @author tycoding 16 | * @since 2021/6/11 17 | */ 18 | @AllArgsConstructor 19 | public class AuthService { 20 | 21 | private final AuthProperties authProperties; 22 | 23 | /** 24 | * 校验当前登录的用户是否拥有指定权限 25 | * 26 | * @param perms 需要校验权限列表 27 | * @return 校验结果 28 | */ 29 | public boolean hasAuth(String... perms) { 30 | if (perms.length == 0) { 31 | return false; 32 | } 33 | // 演示环境禁用操作 34 | if (authProperties.getIsDemoEnv() && AuthUtil.getRoleNames().contains(AuthUtil.DEMO_ENV)) { 35 | return false; 36 | } 37 | 38 | Authentication authentication = AuthUtil.getAuthentication(); 39 | if (authentication == null) { 40 | return false; 41 | } 42 | Collection authorities = authentication.getAuthorities(); 43 | return authorities.stream().map(GrantedAuthority::getAuthority).filter(StringUtils::hasText) 44 | .anyMatch(p -> PatternMatchUtils.simpleMatch(perms, p)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/auth/utils/AuthUtil.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.auth.utils; 2 | 3 | import cn.tycoding.boot.common.core.constant.AuthConstant; 4 | import cn.tycoding.boot.modules.auth.dto.TumoUser; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.security.core.GrantedAuthority; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 10 | import org.springframework.web.context.request.RequestContextHolder; 11 | import org.springframework.web.context.request.ServletRequestAttributes; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.util.ArrayList; 16 | import java.util.Collection; 17 | import java.util.List; 18 | import java.util.Objects; 19 | 20 | /** 21 | * 权限相关方法 22 | * 23 | * @author tycoding 24 | * @since 2021/5/21 25 | */ 26 | public class AuthUtil { 27 | 28 | /** 29 | * 系统预制固定超级管理员角色别名 30 | * 作用:提供一个角色摆脱权限体系的控制,允许词角色访问所有菜单权限 31 | * 使用:所有涉及根据角色查询的地方都排除对此角色的限制 32 | */ 33 | public static final String ADMINISTRATOR = "administrator"; 34 | 35 | /** 36 | * 系统默认演示环境角色别名 37 | * 作用:在 tumo-boot.auth.isDemoEnv 配置开启后,将会对所有按钮级权限拦截并提示前端 38 | */ 39 | public static final String DEMO_ENV = "demo_env"; 40 | 41 | /* 登录表单验证码Key标识 */ 42 | public static final String CAPTCHA_FORM_KEY = "captcha"; 43 | /* 登录验证码Header Key标识 */ 44 | public static final String CAPTCHA_HEADER_KEY = "Captcha-Key"; 45 | /* 验证码错误信息 */ 46 | public static final String CAPTCHA_ERROR_INFO = "验证码不正确"; 47 | /* 没有查询到用户名 */ 48 | public static final String NOT_ROLE_ERROR = "没有查询到用户角色信息"; 49 | 50 | /** 51 | * 获取Request对象 52 | */ 53 | public static HttpServletRequest getRequest() { 54 | return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); 55 | } 56 | 57 | /** 58 | * 获取Response对象 59 | */ 60 | public static HttpServletResponse getResponse() { 61 | return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse(); 62 | } 63 | 64 | /** 65 | * 获取Authentication对象 66 | */ 67 | public static Authentication getAuthentication() { 68 | return SecurityContextHolder.getContext().getAuthentication(); 69 | } 70 | 71 | /** 72 | * 截取前端Token字符串中不包含`Bearer`的部分 73 | */ 74 | public static String getToken(String token) { 75 | if (token != null && token.toLowerCase().startsWith("bearer")) { 76 | return token.replace("bearer", "").trim(); 77 | } 78 | return token; 79 | } 80 | 81 | /** 82 | * 获取用户对象 83 | */ 84 | public static TumoUser getUser() { 85 | Authentication authentication = getAuthentication(); 86 | if (authentication == null) { 87 | return null; 88 | } 89 | Object principal = authentication.getPrincipal(); 90 | if (principal instanceof TumoUser) { 91 | return (TumoUser) principal; 92 | } 93 | return null; 94 | } 95 | 96 | /** 97 | * 获取用户名 98 | */ 99 | public static String getUsername() { 100 | Authentication authentication = getAuthentication(); 101 | if (authentication == null) { 102 | return null; 103 | } 104 | return authentication.getName(); 105 | } 106 | 107 | /** 108 | * 获取登录用户ID 109 | */ 110 | public static Long getUserId() { 111 | TumoUser user = getUser(); 112 | if (user != null) { 113 | return user.getId(); 114 | } 115 | return null; 116 | } 117 | 118 | /** 119 | * 获取用户角色Id集合 120 | */ 121 | public static List getRoleIds() { 122 | List roleIds = new ArrayList<>(); 123 | Authentication authentication = getAuthentication(); 124 | if (authentication == null) { 125 | return roleIds; 126 | } 127 | Collection authorities = authentication.getAuthorities(); 128 | authorities.stream().filter(granted -> StringUtils.startsWith(granted.getAuthority(), AuthConstant.ROLE_PREFIX)).forEach(granted -> { 129 | String id = StringUtils.substringBetween(granted.getAuthority(), AuthConstant.ROLE_PREFIX, AuthConstant.ROLE_SUFFIX); 130 | roleIds.add(Long.parseLong(id)); 131 | }); 132 | return roleIds; 133 | } 134 | 135 | /** 136 | * 获取用户角色Alias集合 137 | */ 138 | public static List getRoleNames() { 139 | List roleNames = new ArrayList<>(); 140 | Authentication authentication = getAuthentication(); 141 | if (authentication == null) { 142 | return roleNames; 143 | } 144 | Collection authorities = authentication.getAuthorities(); 145 | authorities.stream().filter(granted -> StringUtils.startsWith(granted.getAuthority(), AuthConstant.ROLE_PREFIX)).forEach(granted -> { 146 | String name = StringUtils.substringAfter(granted.getAuthority(), AuthConstant.ROLE_SUFFIX); 147 | roleNames.add(name); 148 | }); 149 | return roleNames; 150 | } 151 | 152 | /** 153 | * 密码加密 154 | * 155 | * @param password password为空则用默认密码加密 156 | */ 157 | public static String encode(String password) { 158 | BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); 159 | if (password == null || password.isEmpty()) { 160 | return passwordEncoder.encode(AuthConstant.DEFAULT_PASS); 161 | } 162 | return passwordEncoder.encode(password); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/api/HttpCode.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.api; 2 | 3 | /** 4 | * 统一定义请求响应状态码 5 | * 6 | * @author tycoding 7 | * @since 2021/5/21 8 | */ 9 | @SuppressWarnings("all") 10 | public enum HttpCode { 11 | SUCCESS(200, "操作成功"), 12 | FAILURE(400, "业务异常"), 13 | UN_AUTHORIZED(401, "请求未授权"), 14 | AUTH_UN_AUTHORIZED(400, "用户名或密码错误"), 15 | CLIENT_UN_AUTHORIZED(401, "客户端请求未授权"), 16 | INVALID_GRANT(401, "用户名或密码错误"), 17 | FORBIDDEN(403, "没有访问权限"), 18 | NOT_FOUND(404, "404 没找到请求"), 19 | MSG_NOT_READABLE(400, "消息不能读取"), 20 | METHOD_NOT_SUPPORTED(405, "不支持当前请求方法"), 21 | MEDIA_TYPE_NOT_SUPPORTED(415, "不支持当前媒体类型"), 22 | REQ_REJECT(403, "请求被拒绝"), 23 | INTERNAL_SERVER_ERROR(500, "服务器异常"), 24 | PARAM_MISS(400, "缺少必要的请求参数"), 25 | PARAM_TYPE_ERROR(400, "请求参数类型错误"), 26 | PARAM_BIND_ERROR(400, "请求参数绑定错误"), 27 | PARAM_VALID_ERROR(400, "参数校验失败"); 28 | 29 | final int code; 30 | final String msg; 31 | 32 | public int getCode() { 33 | return this.code; 34 | } 35 | 36 | public String getMsg() { 37 | return this.msg; 38 | } 39 | 40 | private HttpCode(final int code, final String msg) { 41 | this.code = code; 42 | this.msg = msg; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/api/QueryPage.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.api; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 统一定义分页查询接口分页格式 10 | * 11 | * @author tycoding 12 | * @since 2021/5/21 13 | */ 14 | @Data 15 | @AllArgsConstructor 16 | public class QueryPage implements Serializable { 17 | private static final long serialVersionUID = 1L; 18 | 19 | /** 20 | * 当前页 21 | */ 22 | private int page; 23 | 24 | /** 25 | * 每页的记录数 26 | */ 27 | private int limit; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/api/R.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.api; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 统一定义请求响应数据格式 9 | * 10 | * @author tycoding 11 | * @since 2021/5/21 12 | */ 13 | @Data 14 | public class R implements Serializable { 15 | private static final long serialVersionUID = 1L; 16 | 17 | private int code = HttpCode.SUCCESS.code; 18 | 19 | private String msg = HttpCode.SUCCESS.msg; 20 | 21 | private T data; 22 | 23 | public R() { 24 | super(); 25 | } 26 | 27 | public R(int code, String msg) { 28 | this.code = code; 29 | this.msg = msg; 30 | } 31 | 32 | public static R ok(T data) { 33 | return new R(data); 34 | } 35 | 36 | public static R ok(T data, HttpCode httpCode) { 37 | return new R(data, httpCode); 38 | } 39 | 40 | public static R ok() { 41 | return new R<>(); 42 | } 43 | 44 | public static R ok(String msg) { 45 | return new R<>(HttpCode.SUCCESS.getCode(), msg); 46 | } 47 | 48 | public static R ok(HttpCode httpCode) { 49 | return new R<>(httpCode); 50 | } 51 | 52 | public static R fail(String msg) { 53 | return new R<>(HttpCode.FAILURE.getCode(), msg); 54 | } 55 | 56 | public static R fail(int code, String msg) { 57 | return new R<>(code, msg); 58 | } 59 | 60 | public static R fail(HttpCode httpCode) { 61 | return new R<>(httpCode); 62 | } 63 | 64 | public static R fail(Throwable e) { 65 | return new R<>(e); 66 | } 67 | 68 | protected R(T data) { 69 | this.data = data; 70 | } 71 | 72 | protected R(HttpCode httpCode) { 73 | this.code = httpCode.code; 74 | this.msg = httpCode.msg; 75 | } 76 | 77 | protected R(T data, HttpCode httpCode) { 78 | this.data = data; 79 | this.code = httpCode.code; 80 | this.msg = httpCode.msg; 81 | } 82 | 83 | protected R(Throwable e) { 84 | super(); 85 | this.code = HttpCode.INTERNAL_SERVER_ERROR.code; 86 | this.msg = e.getMessage(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/config/JacksonConfiguration.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.config; 2 | 3 | import cn.hutool.core.date.DatePattern; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; 6 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; 7 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; 8 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 9 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 11 | import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; 12 | import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | 16 | import java.time.LocalDate; 17 | import java.time.LocalDateTime; 18 | import java.time.LocalTime; 19 | import java.time.ZoneId; 20 | import java.time.format.DateTimeFormatter; 21 | import java.util.Locale; 22 | import java.util.TimeZone; 23 | 24 | /** 25 | * 全局配置Jackson序列化 26 | * 27 | * @author tycoding 28 | * @since 2021/5/21 29 | */ 30 | @Configuration(proxyBeanMethods = false) 31 | @ConditionalOnClass(ObjectMapper.class) 32 | @AutoConfigureBefore(JacksonAutoConfiguration.class) 33 | public class JacksonConfiguration { 34 | 35 | @Bean 36 | public Jackson2ObjectMapperBuilderCustomizer customizer() { 37 | return builder -> { 38 | // 本地化 39 | builder.locale(Locale.CHINA); 40 | // 时区 41 | builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault())); 42 | // 全局Long类型序列化为字符串 43 | builder.serializerByType(Long.class, ToStringSerializer.instance); 44 | 45 | // 时间类型序列化 46 | builder.simpleDateFormat(DatePattern.NORM_DATETIME_PATTERN); 47 | builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN))); 48 | builder.serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN))); 49 | builder.serializerByType(LocalTime.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN))); 50 | 51 | builder.deserializerByType(LocalDateTime.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN))); 52 | builder.deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN))); 53 | builder.deserializerByType(LocalTime.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN))); 54 | }; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/constant/ApiConstant.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.constant; 2 | 3 | /** 4 | * API接口常量值 5 | * 6 | * @author tycoding 7 | * @since 2021/5/21 8 | */ 9 | public interface ApiConstant { 10 | 11 | /** 12 | * 基础API接口前缀 13 | */ 14 | String API_BASE = "/tumo-boot"; 15 | 16 | /** 17 | * 自定义OAuth Token端点地址 18 | */ 19 | String API_OAUTH_TOKEN = "/tumo-boot/auth/oauth/token"; 20 | 21 | /** 22 | * API接口前缀 - Auth模块 23 | */ 24 | String API_AUTH_PREFIX = "/tumo-boot/auth"; 25 | 26 | /** 27 | * API接口前缀 - Upms模块 28 | */ 29 | String API_UPMS_PREFIX = "/tumo-boot/upms"; 30 | 31 | /** 32 | * API接口前缀 - System模块 33 | */ 34 | String API_SYSTEM_PREFIX = "/tumo-boot/system"; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/constant/AuthConstant.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.constant; 2 | 3 | /** 4 | * OAuth2 存储数据前缀 5 | * 6 | * @author tycoding 7 | * @since 2021/5/21 8 | */ 9 | public interface AuthConstant { 10 | 11 | /** 12 | * 角色前缀名 13 | */ 14 | String ROLE_PREFIX = "ROLE_PREFIX_"; 15 | 16 | /** 17 | * 角色后缀名 18 | */ 19 | String ROLE_SUFFIX = "_ROLE_SUFFIX_"; 20 | 21 | /** 22 | * 默认密码(重置密码) 23 | */ 24 | String DEFAULT_PASS = "123456"; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/constant/CacheConstant.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.constant; 2 | 3 | /** 4 | * 缓存常量 5 | * 6 | * @author tycoding 7 | * @since 2021/5/21 8 | */ 9 | public interface CacheConstant { 10 | 11 | /** 12 | * 系统所有Redis缓存Key前缀 prefix 13 | */ 14 | String REDIS_KEY_PREFIX = "tumo-boot:"; 15 | 16 | /** 17 | * OAuth缓存前缀 18 | */ 19 | String OAUTH_PREFIX = REDIS_KEY_PREFIX + "auth:oauth:"; 20 | 21 | /** 22 | * 验证码缓存前缀 23 | */ 24 | String CAPTCHA_PREFIX = REDIS_KEY_PREFIX + "auth:captcha:"; 25 | 26 | /** 27 | * 用户信息缓存 28 | */ 29 | String USER_DETAIL_KEY = REDIS_KEY_PREFIX + "user_details"; 30 | 31 | /** 32 | * 菜单权限缓存 33 | */ 34 | String MENU_DETAIL_KEY = REDIS_KEY_PREFIX + "menu_details"; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/constant/CaptchaConstant.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.constant; 2 | 3 | /** 4 | * 图片验证码常量 5 | * 6 | * @author tycoding 7 | * @since 2021/5/21 8 | */ 9 | public interface CaptchaConstant { 10 | 11 | /** 12 | * 验证码图片高度 13 | */ 14 | int CAPTCHA_HEIGHT = 32; 15 | 16 | /** 17 | * 验证码图片宽度 18 | */ 19 | int CAPTCHA_WIDTH = 110; 20 | 21 | /** 22 | * 验证码图片字符数量 23 | */ 24 | int CAPTCHA_COUNT = 4; 25 | 26 | /** 27 | * 验证码图片干扰线数量 28 | */ 29 | int CAPTCHA_CIRCLE_COUNT = 10; 30 | 31 | /** 32 | * 验证码过期时间(分钟) 33 | */ 34 | int CAPTCHA_TIMEOUT = 10; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/constant/CommonConstant.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.constant; 2 | 3 | /** 4 | * 系统公共常量 5 | * 6 | * @author tycoding 7 | * @since 2021/5/21 8 | */ 9 | public interface CommonConstant { 10 | 11 | /** 12 | * UTF-8 编码 13 | */ 14 | String UTF_8 = "utf-8"; 15 | 16 | /** 17 | * JSON 请求响应格式 18 | */ 19 | String CONTENT_TYPE = "application/json; charset=utf-8"; 20 | 21 | /** 22 | * 菜单类型:menu 23 | */ 24 | String MENU_TYPE_MENU = "menu"; 25 | 26 | /** 27 | * 菜单类型:button 28 | */ 29 | String MENU_TYPE_BUTTON = "button"; 30 | 31 | /** 32 | * 菜单:默认Icon图标 33 | */ 34 | String MENU_ICON = "alert"; 35 | 36 | /** 37 | * 默认用户头像路径 38 | */ 39 | String DEFAULT_AVATAR = "/default.png"; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/launch/StartEventListener.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.launch; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.boot.web.context.WebServerInitializedEvent; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.event.EventListener; 7 | import org.springframework.core.env.Environment; 8 | import org.springframework.scheduling.annotation.Async; 9 | import org.springframework.util.StringUtils; 10 | 11 | import java.net.UnknownHostException; 12 | 13 | /** 14 | * 自定义启动日志 15 | * 16 | * @author tycoding 17 | * @since 2021/5/21 18 | */ 19 | @Slf4j 20 | @Configuration 21 | public class StartEventListener { 22 | 23 | public StartEventListener() { 24 | } 25 | 26 | @Async 27 | @EventListener({WebServerInitializedEvent.class}) 28 | public void afterStart(WebServerInitializedEvent event) throws UnknownHostException { 29 | Environment environment = event.getApplicationContext().getEnvironment(); 30 | String appName = environment.getProperty("spring.application.name"); 31 | int port = event.getWebServer().getPort(); 32 | String profile = StringUtils.arrayToCommaDelimitedString(environment.getActiveProfiles()); 33 | log.info("----[{}]----启动完成。当前使用端口:[{}],环境变量:[{}]", appName, port, profile); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/utils/BeanUtil.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.utils; 2 | 3 | import ma.glasnost.orika.MapperFacade; 4 | import ma.glasnost.orika.impl.DefaultMapperFactory; 5 | 6 | import java.util.Collection; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | /** 11 | * Orika Bean拷贝工具的简单封装 12 | * 13 | * @author tycoding 14 | * @since 2021/5/21 15 | */ 16 | public class BeanUtil { 17 | 18 | private static final MapperFacade MAPPER; 19 | 20 | static { 21 | DefaultMapperFactory factory = new DefaultMapperFactory.Builder().build(); 22 | MAPPER = factory.getMapperFacade(); 23 | } 24 | 25 | public static T copy(Object source, T target) { 26 | if (source == null) { 27 | return null; 28 | } 29 | MAPPER.map(source, target); 30 | return target; 31 | } 32 | 33 | public static T copy(Object source, Class targetClass) { 34 | if (source == null) { 35 | return null; 36 | } 37 | return MAPPER.map(source, targetClass); 38 | } 39 | 40 | public static List copy(Collection source, Class targetClass) { 41 | if (source != null && source.size() > 0) { 42 | return MAPPER.mapAsList(source, targetClass); 43 | } else { 44 | return Collections.emptyList(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/utils/IpUtil.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.utils; 2 | 3 | import java.net.InetAddress; 4 | import java.net.NetworkInterface; 5 | import java.util.Enumeration; 6 | 7 | /** 8 | * 获取服务IP地址 9 | * 10 | * @author tycoding 11 | * @since 2021/5/26 12 | */ 13 | public class IpUtil { 14 | 15 | public static InetAddress getAddress() { 16 | try { 17 | InetAddress candidateAddress = null; 18 | 19 | Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); 20 | while (networkInterfaces.hasMoreElements()) { 21 | NetworkInterface iface = networkInterfaces.nextElement(); 22 | // 该网卡接口下的ip会有多个,也需要一个个的遍历,找到自己所需要的 23 | for (Enumeration inetAddrs = iface.getInetAddresses(); inetAddrs.hasMoreElements(); ) { 24 | InetAddress inetAddr = inetAddrs.nextElement(); 25 | // 排除loopback回环类型地址(不管是IPv4还是IPv6 只要是回环地址都会返回true) 26 | if (!inetAddr.isLoopbackAddress()) { 27 | if (inetAddr.isSiteLocalAddress()) { 28 | // 如果是site-local地址,就是它了 就是我们要找的 29 | // ~~~~~~~~~~~~~绝大部分情况下都会在此处返回你的ip地址值~~~~~~~~~~~~~ 30 | return inetAddr; 31 | } 32 | 33 | // 若不是site-local地址 那就记录下该地址当作候选 34 | if (candidateAddress == null) { 35 | candidateAddress = inetAddr; 36 | } 37 | } 38 | } 39 | } 40 | 41 | // 如果出去loopback回环地之外无其它地址了,那就回退到原始方案吧 42 | return candidateAddress == null ? InetAddress.getLocalHost() : candidateAddress; 43 | } catch (Exception e) { 44 | e.printStackTrace(); 45 | } 46 | return null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/utils/Is.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.utils; 2 | 3 | import java.util.List; 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * 校验工具类 8 | * 9 | * @author tycoding 10 | * @since 2021/5/21 11 | */ 12 | public class Is { 13 | 14 | public static boolean isUrl(String url) { 15 | String reg = "(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\'\\/\\\\&%\\+\\$#_=]*)?"; 16 | Pattern pattern = Pattern.compile(reg); 17 | return pattern.matcher(url).matches(); 18 | } 19 | 20 | public static boolean isEmpty(List list) { 21 | return list == null || list.size() == 0; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/core/utils/ServletUtil.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.core.utils; 2 | 3 | import cn.hutool.json.JSONUtil; 4 | import cn.tycoding.boot.common.core.api.R; 5 | import cn.tycoding.boot.common.core.constant.CommonConstant; 6 | import lombok.SneakyThrows; 7 | 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | /** 11 | * Servlet工具类 12 | * 13 | * @author tycoding 14 | * @since 2021/5/21 15 | */ 16 | public class ServletUtil { 17 | 18 | @SneakyThrows 19 | public static void write(HttpServletResponse response, R data) { 20 | response.setStatus(data.getCode()); 21 | response.setHeader("Content-type", "application/json;charset=" + CommonConstant.UTF_8); 22 | response.setCharacterEncoding(CommonConstant.UTF_8); 23 | response.getWriter().write(JSONUtil.toJsonStr(data)); 24 | } 25 | 26 | @SneakyThrows 27 | public static void write(HttpServletResponse response, int status, R data) { 28 | response.setStatus(status); 29 | response.setHeader("Content-type", "application/json;charset=" + CommonConstant.UTF_8); 30 | response.setCharacterEncoding(CommonConstant.UTF_8); 31 | response.getWriter().write(JSONUtil.toJsonStr(data)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/log/LogAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.log; 2 | 3 | import cn.tycoding.boot.common.log.aspect.ApiLogAspect; 4 | import cn.tycoding.boot.common.log.event.LogListener; 5 | import cn.tycoding.boot.common.log.props.LogProperties; 6 | import cn.tycoding.boot.modules.system.service.SysLogService; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; 9 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | /** 14 | * Log模块配置注入 15 | * 16 | * @author tycoding 17 | * @since 2021/5/21 18 | */ 19 | @Configuration 20 | @RequiredArgsConstructor 21 | @ConditionalOnWebApplication 22 | @EnableConfigurationProperties({LogProperties.class}) 23 | public class LogAutoConfiguration { 24 | 25 | @Bean 26 | public LogListener logListener(SysLogService sysLogService) { 27 | return new LogListener(sysLogService); 28 | } 29 | 30 | @Bean 31 | public ApiLogAspect logAspect() { 32 | return new ApiLogAspect(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/log/annotation/ApiLog.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.log.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 自定义日志记录切面注解 10 | * 11 | * @author tycoding 12 | * @since 2021/5/21 13 | */ 14 | @Target(ElementType.METHOD) 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface ApiLog { 17 | 18 | String value() default ""; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/log/aspect/ApiLogAspect.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.log.aspect; 2 | 3 | import cn.tycoding.boot.common.log.utils.SpringContextHolder; 4 | import cn.tycoding.boot.common.log.event.LogEvent; 5 | import cn.tycoding.boot.common.log.utils.SysLogUtil; 6 | import cn.tycoding.boot.modules.system.entity.SysLog; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.aspectj.lang.ProceedingJoinPoint; 9 | import org.aspectj.lang.annotation.Around; 10 | import org.aspectj.lang.annotation.Aspect; 11 | 12 | /** 13 | * 自定义日志记录切面 14 | * 15 | * @author tycoding 16 | * @since 2021/5/21 17 | */ 18 | @Slf4j 19 | @Aspect 20 | public class ApiLogAspect { 21 | 22 | @Around("@annotation(apiLog)") 23 | public Object around(ProceedingJoinPoint point, cn.tycoding.boot.common.log.annotation.ApiLog apiLog) throws Throwable { 24 | String className = point.getTarget().getClass().getName(); 25 | String methodName = point.getSignature().getName(); 26 | 27 | long beginTime = System.currentTimeMillis(); 28 | long time = System.currentTimeMillis() - beginTime; 29 | 30 | String method = className + "." + methodName + "()"; 31 | SysLog sysLog = SysLogUtil.build(SysLogUtil.TYPE_OK, apiLog.value(), method, time); 32 | 33 | SpringContextHolder.publishEvent(new LogEvent(sysLog)); 34 | return point.proceed(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/log/aspect/RequestLogAspect.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.log.aspect; 2 | 3 | import cn.hutool.json.JSONObject; 4 | import cn.tycoding.boot.common.auth.utils.AuthUtil; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.aspectj.lang.ProceedingJoinPoint; 7 | import org.aspectj.lang.annotation.Around; 8 | import org.aspectj.lang.annotation.Aspect; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.http.HttpHeaders; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | 15 | /** 16 | * 全局拦截Controller接口,打印请求日志 17 | * 18 | * @author tycoding 19 | * @since 2021/5/21 20 | */ 21 | @Slf4j 22 | @Aspect 23 | @Configuration 24 | @ConditionalOnProperty(value = {"tumo-boot.log.enable"}, matchIfMissing = true) 25 | public class RequestLogAspect { 26 | 27 | @Around("(@within(org.springframework.security.oauth2.provider.endpoint.FrameworkEndpoint)) || (execution(!static cn.tycoding.boot.common.core.api.R *(..)) && (@within(org.springframework.stereotype.Controller) || @within(org.springframework.web.bind.annotation.RestController)))") 28 | public Object around(ProceedingJoinPoint point) throws Throwable { 29 | HttpServletRequest request = AuthUtil.getRequest(); 30 | String beforeLog = "\n\n================ Request Start ================" + 31 | "\n===General=== Request URL: " + request.getRequestURL() + 32 | "\n===General=== Request Method: " + request.getMethod() + 33 | "\n===General=== Remote Address: " + request.getRemoteAddr() + 34 | "\n\n===Headers=== Accept: " + request.getHeader(HttpHeaders.ACCEPT) + 35 | "\n===Headers=== Authorization: " + request.getHeader(HttpHeaders.AUTHORIZATION) + 36 | "\n===Headers=== Host: " + request.getHeader(HttpHeaders.HOST) + 37 | "\n===Headers=== User-Agent: " + request.getHeader(HttpHeaders.USER_AGENT) + 38 | "\n===Headers=== Parameters: " + new JSONObject(request.getParameterMap()).toString() + 39 | // 打印响应结果,内容过长会影响效率 40 | // "\n\n===Result=== " + new JSONObject(result).toString() + 41 | "\n================ Request End ================\n"; 42 | log.info(beforeLog); 43 | return point.proceed(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/log/event/LogEvent.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.log.event; 2 | 3 | import org.springframework.context.ApplicationEvent; 4 | 5 | /** 6 | * 自定义定义 Log事件 7 | * 8 | * @author tycoding 9 | * @since 2021/5/21 10 | */ 11 | public class LogEvent extends ApplicationEvent { 12 | 13 | public LogEvent(Object source) { 14 | super(source); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/log/event/LogListener.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.log.event; 2 | 3 | import cn.tycoding.boot.modules.system.entity.SysLog; 4 | import cn.tycoding.boot.modules.system.service.SysLogService; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.context.event.EventListener; 7 | import org.springframework.core.annotation.Order; 8 | import org.springframework.scheduling.annotation.Async; 9 | 10 | /** 11 | * 监听自定义Log 事件 12 | * 13 | * @author tycoding 14 | * @since 2021/5/21 15 | */ 16 | @RequiredArgsConstructor 17 | public class LogListener { 18 | 19 | private final SysLogService sysLogService; 20 | 21 | @Async 22 | @Order 23 | @EventListener(LogEvent.class) 24 | public void saveLog(LogEvent event) { 25 | SysLog sysLog = (SysLog) event.getSource(); 26 | sysLogService.add(sysLog); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/log/exception/GlobalExceptionTranslator.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.log.exception; 2 | 3 | import cn.tycoding.boot.common.auth.props.AuthProperties; 4 | import cn.tycoding.boot.common.auth.utils.AuthUtil; 5 | import cn.tycoding.boot.common.core.api.HttpCode; 6 | import cn.tycoding.boot.common.core.api.R; 7 | import cn.tycoding.boot.common.log.utils.SysLogUtil; 8 | import cn.tycoding.boot.modules.auth.exception.TumoAuth2Exception; 9 | import io.lettuce.core.RedisConnectionException; 10 | import lombok.RequiredArgsConstructor; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.data.redis.RedisConnectionFailureException; 13 | import org.springframework.http.HttpStatus; 14 | import org.springframework.security.access.AccessDeniedException; 15 | import org.springframework.web.bind.annotation.ExceptionHandler; 16 | import org.springframework.web.bind.annotation.ResponseStatus; 17 | import org.springframework.web.bind.annotation.RestControllerAdvice; 18 | import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; 19 | 20 | /** 21 | * 全局异常拦截(注意:这种方式只能拦截经过Controller的异常,未经过Controller的异常拦截不到) 22 | * 23 | * @author tycoding 24 | * @since 2021/5/21 25 | */ 26 | @Slf4j 27 | @RequiredArgsConstructor 28 | @RestControllerAdvice 29 | public class GlobalExceptionTranslator { 30 | 31 | private final AuthProperties authProperties; 32 | 33 | @ExceptionHandler({ServiceException.class}) 34 | @ResponseStatus(HttpStatus.BAD_REQUEST) 35 | public R handleError(ServiceException e) { 36 | log.error("----------业务异常----------"); 37 | e.printStackTrace(); 38 | SysLogUtil.publish(SysLogUtil.TYPE_FAIL, "业务异常"); 39 | return R.fail(e.getHttpCode().getCode(), e.getMessage()); 40 | } 41 | 42 | @ExceptionHandler({AccessDeniedException.class}) 43 | @ResponseStatus(HttpStatus.FORBIDDEN) 44 | public R handleError(AccessDeniedException e) { 45 | log.error("----------没有访问权限----------"); 46 | e.printStackTrace(); 47 | if (authProperties.getIsDemoEnv() && AuthUtil.getRoleNames().contains(AuthUtil.DEMO_ENV)) { 48 | SysLogUtil.publish(SysLogUtil.TYPE_FAIL, "演示环境,请勿操作!"); 49 | return R.fail(HttpCode.FORBIDDEN.getCode(), "演示环境,请勿操作!"); 50 | } 51 | SysLogUtil.publish(SysLogUtil.TYPE_FAIL, "没有访问权限"); 52 | return R.fail(HttpCode.FORBIDDEN); 53 | } 54 | 55 | @ExceptionHandler({TumoAuth2Exception.class}) 56 | @ResponseStatus(HttpStatus.UNAUTHORIZED) 57 | public R handleError(TumoAuth2Exception e) { 58 | log.error("----------认证异常----------"); 59 | e.printStackTrace(); 60 | SysLogUtil.publish(SysLogUtil.TYPE_FAIL, "认证异常"); 61 | return R.fail(e); 62 | } 63 | 64 | @ExceptionHandler({RedisConnectionFailureException.class, RedisConnectionException.class}) 65 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 66 | public R handleError(RedisConnectionFailureException e) { 67 | log.error("----------Redis连接异常----------"); 68 | SysLogUtil.publish(SysLogUtil.TYPE_FAIL, "Redis连接异常"); 69 | return R.fail("Redis连接异常"); 70 | } 71 | 72 | @ExceptionHandler({Exception.class}) 73 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 74 | public R handleError(Exception e) { 75 | log.error("----------服务器异常----------"); 76 | e.printStackTrace(); 77 | SysLogUtil.publish(SysLogUtil.TYPE_FAIL, "服务器异常"); 78 | return R.fail(e); 79 | } 80 | 81 | @ExceptionHandler({MethodArgumentTypeMismatchException.class}) 82 | @ResponseStatus(HttpStatus.BAD_REQUEST) 83 | public R handleError(MethodArgumentTypeMismatchException e) { 84 | log.error("----------参数类型不匹配异常----------"); 85 | e.printStackTrace(); 86 | return R.fail(e); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/log/exception/RestExceptionTranslator.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.log.exception; 2 | 3 | import cn.tycoding.boot.common.core.api.HttpCode; 4 | import cn.tycoding.boot.common.core.api.R; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.web.bind.annotation.ExceptionHandler; 10 | import org.springframework.web.bind.annotation.ResponseStatus; 11 | import org.springframework.web.bind.annotation.RestControllerAdvice; 12 | import org.springframework.web.servlet.DispatcherServlet; 13 | import org.springframework.web.servlet.NoHandlerFoundException; 14 | 15 | import javax.servlet.Servlet; 16 | 17 | /** 18 | * 自定义Rest请求异常 19 | * 20 | * @author tycoding 21 | * @since 2021/5/21 22 | */ 23 | @Slf4j 24 | @Configuration 25 | @ConditionalOnClass({Servlet.class, DispatcherServlet.class}) 26 | @RestControllerAdvice 27 | public class RestExceptionTranslator { 28 | 29 | public RestExceptionTranslator() { 30 | } 31 | 32 | @ExceptionHandler({NoHandlerFoundException.class}) 33 | @ResponseStatus(HttpStatus.NOT_FOUND) 34 | public R handleError(NoHandlerFoundException e) { 35 | log.error(HttpCode.NOT_FOUND.getMsg(), e.getMessage()); 36 | return R.fail(HttpCode.NOT_FOUND); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/log/exception/ServiceException.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.log.exception; 2 | 3 | import cn.tycoding.boot.common.core.api.HttpCode; 4 | import lombok.Getter; 5 | 6 | /** 7 | * 自定义异常 8 | * 9 | * @author tycoding 10 | * @since 2021/6/10 11 | */ 12 | public class ServiceException extends RuntimeException { 13 | private static final long serialVersionUID = -1068765335343416833L; 14 | 15 | @Getter 16 | private final HttpCode httpCode; 17 | 18 | public ServiceException(String message) { 19 | super(message); 20 | this.httpCode = HttpCode.FAILURE; 21 | } 22 | 23 | public ServiceException(HttpCode httpCode) { 24 | this.httpCode = httpCode; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/log/props/LogProperties.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.log.props; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * 自定义Log模块 YML配置映射实体 8 | * 9 | * @author tycoding 10 | * @since 2021/5/21 11 | */ 12 | @Data 13 | @ConfigurationProperties("tumo-boot.log") 14 | public class LogProperties { 15 | 16 | /** 17 | * 是否打印API请求日志 18 | */ 19 | private Boolean enable; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/log/utils/SpringContextHolder.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.log.utils; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.context.ApplicationEvent; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | * 自定义Spring事件监听 11 | * 12 | * @author tycoding 13 | * @since 2021/5/21 14 | */ 15 | @Service 16 | public class SpringContextHolder implements ApplicationContextAware { 17 | 18 | private static ApplicationContext applicationContext = null; 19 | 20 | @Override 21 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 22 | SpringContextHolder.applicationContext = applicationContext; 23 | } 24 | 25 | public static void publishEvent(ApplicationEvent event) { 26 | if (applicationContext == null) { 27 | return; 28 | } 29 | applicationContext.publishEvent(event); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/log/utils/SysLogUtil.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.log.utils; 2 | 3 | import cn.hutool.core.util.URLUtil; 4 | import cn.hutool.extra.servlet.ServletUtil; 5 | import cn.hutool.http.HttpUtil; 6 | import cn.tycoding.boot.common.auth.utils.AuthUtil; 7 | import cn.tycoding.boot.common.log.event.LogEvent; 8 | import cn.tycoding.boot.modules.system.entity.SysLog; 9 | import lombok.SneakyThrows; 10 | import org.springframework.web.context.request.RequestContextHolder; 11 | import org.springframework.web.context.request.ServletRequestAttributes; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import java.util.Date; 15 | import java.util.Objects; 16 | 17 | /** 18 | * 构建Log实体类信息 19 | * 20 | * @author tycoding 21 | * @since 2021/5/21 22 | */ 23 | public class SysLogUtil { 24 | 25 | /** 26 | * 字典表类型标识 27 | */ 28 | public static final String DICT_TYPE = "sys_type"; 29 | /** 30 | * 成功日志类型 31 | */ 32 | public static final int TYPE_OK = 1; 33 | /** 34 | * 错误日志类型 35 | */ 36 | public static final int TYPE_FAIL = 2; 37 | 38 | /** 39 | * 构建日志Log类信息 40 | * 41 | * @param type 日志类型 42 | * @param operation 日志描述 43 | * @param method 操作方法 44 | * @param time 耗时 45 | * @return Log类 46 | */ 47 | @SneakyThrows 48 | public static SysLog build(Integer type, String operation, String method, Long time) { 49 | HttpServletRequest request = ((ServletRequestAttributes) 50 | Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); 51 | 52 | return new SysLog() 53 | .setType(type) 54 | .setUsername(AuthUtil.getUsername()) 55 | .setOperation(operation) 56 | .setCreateTime(new Date()) 57 | .setIp(ServletUtil.getClientIP(request)) 58 | .setUrl(URLUtil.getPath(request.getRequestURI())) 59 | .setMethod(method) 60 | .setParams(HttpUtil.toParams(request.getParameterMap())) 61 | .setUserAgent(request.getHeader("user-agent")) 62 | .setTime(time); 63 | } 64 | 65 | /** 66 | * Spring事件发布:发布日志,写入到数据库 67 | * 68 | * @param type 日志类型 69 | * @param operation 描述 70 | */ 71 | public static void publish(int type, String operation) { 72 | SysLog sysLog = SysLogUtil.build(type, operation, null, null); 73 | SpringContextHolder.publishEvent(new LogEvent(sysLog)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/mybatis/MybatisAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.mybatis; 2 | 3 | import cn.tycoding.boot.common.mybatis.props.MybatisProperties; 4 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.core.annotation.Order; 7 | 8 | /** 9 | * Mybatis配置注入 10 | * 11 | * @author tycoding 12 | * @since 2021/5/21 13 | */ 14 | @Order 15 | @Configuration 16 | @EnableConfigurationProperties({MybatisProperties.class}) 17 | public class MybatisAutoConfiguration { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/mybatis/config/MybatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.mybatis.config; 2 | 3 | import cn.tycoding.boot.common.mybatis.intercept.SqlLogInterceptor; 4 | import com.baomidou.mybatisplus.annotation.DbType; 5 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 6 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | /** 12 | * MybatisPlus插件配置 13 | * 14 | * @author tycoding 15 | * @since 2021/5/21 16 | */ 17 | @Configuration 18 | public class MybatisPlusConfig { 19 | 20 | /** 21 | * 分页插件 22 | */ 23 | @Bean 24 | public MybatisPlusInterceptor mybatisPlusInterceptor() { 25 | MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 26 | interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); 27 | return interceptor; 28 | } 29 | 30 | /** 31 | * SQL 日志打印 32 | */ 33 | @Bean 34 | @ConditionalOnProperty(value = {"tumo-boot.mybatis.enable"}, matchIfMissing = true) 35 | public SqlLogInterceptor sqlLogInterceptor() { 36 | return new SqlLogInterceptor(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/mybatis/constant/MybatisConstant.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.mybatis.constant; 2 | 3 | /** 4 | * 针对MybatisPlus的常量 5 | * 6 | * @author tycoding 7 | * @since 2021/5/21 8 | */ 9 | public interface MybatisConstant { 10 | 11 | /** 12 | * 分页结果集中承载分页数据的属性名 13 | */ 14 | String PAGE_ROWS = "rows"; 15 | 16 | /** 17 | * 分页结果集中总数量total名称 18 | */ 19 | String PAGE_TOTAL = "total"; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/mybatis/intercept/SqlLogInterceptor.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.mybatis.intercept; 2 | 3 | import cn.hutool.core.text.StrFormatter; 4 | import cn.hutool.core.util.StrUtil; 5 | import com.baomidou.mybatisplus.core.toolkit.PluginUtils; 6 | import com.baomidou.mybatisplus.core.toolkit.SystemClock; 7 | import org.apache.ibatis.executor.statement.StatementHandler; 8 | import org.apache.ibatis.mapping.MappedStatement; 9 | import org.apache.ibatis.plugin.Interceptor; 10 | import org.apache.ibatis.plugin.Intercepts; 11 | import org.apache.ibatis.plugin.Invocation; 12 | import org.apache.ibatis.plugin.Signature; 13 | import org.apache.ibatis.reflection.MetaObject; 14 | import org.apache.ibatis.reflection.SystemMetaObject; 15 | import org.apache.ibatis.session.ResultHandler; 16 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 17 | 18 | import java.lang.reflect.Method; 19 | import java.lang.reflect.Proxy; 20 | import java.sql.Statement; 21 | 22 | /** 23 | * SQL 拦截 24 | * 25 | * @author tycoding 26 | * @since 2021/5/21 27 | */ 28 | @Intercepts({@Signature( 29 | type = StatementHandler.class, 30 | method = "query", 31 | args = {Statement.class, ResultHandler.class} 32 | )}) 33 | public class SqlLogInterceptor implements Interceptor { 34 | 35 | private Method druidGetSqlMethod; 36 | 37 | @Override 38 | public Object intercept(Invocation invocation) throws Throwable { 39 | Object firstArg = invocation.getArgs()[0]; 40 | Statement statement; 41 | if (Proxy.isProxyClass(firstArg.getClass())) { 42 | statement = (Statement) SystemMetaObject.forObject(firstArg).getValue("h.statement"); 43 | } else { 44 | statement = (Statement) firstArg; 45 | } 46 | 47 | String originalSql = null; 48 | String stmtClassName = statement.getClass().getName(); 49 | Class clazz; 50 | Object stmtSql; 51 | if ("com.alibaba.druid.pool.DruidPooledPreparedStatement".equals(stmtClassName)) { 52 | try { 53 | if (this.druidGetSqlMethod == null) { 54 | clazz = Class.forName("com.alibaba.druid.pool.DruidPooledPreparedStatement"); 55 | this.druidGetSqlMethod = clazz.getMethod("getSql"); 56 | } 57 | 58 | stmtSql = this.druidGetSqlMethod.invoke(statement); 59 | if (stmtSql instanceof String) { 60 | originalSql = (String) stmtSql; 61 | } 62 | } catch (Exception e) { 63 | e.printStackTrace(); 64 | } 65 | } 66 | 67 | if (originalSql == null) { 68 | originalSql = statement.toString(); 69 | } 70 | 71 | long start = SystemClock.now(); 72 | Object result = invocation.proceed(); 73 | long timing = SystemClock.now() - start; 74 | Object target = PluginUtils.realTarget(invocation.getTarget()); 75 | MetaObject metaObject = SystemMetaObject.forObject(target); 76 | MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); 77 | System.err.println(StrFormatter.format("\n============== Sql Start ==============\nExecute ID :{}\nExecute SQL :{}\nExecute Time:{} ms\n============== Sql End ==============\n", ms.getId(), StrUtil.replaceChars(originalSql, "\n", " "), timing)); 78 | return result; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/mybatis/props/MybatisProperties.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.mybatis.props; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * 自定义Mybatis模块 YML配置映射实体 8 | * 9 | * @author tycoding 10 | * @since 2021/5/21 11 | */ 12 | @Data 13 | @ConfigurationProperties("tumo-boot.mybatis") 14 | public class MybatisProperties { 15 | 16 | /** 17 | * 是否打印Mybatis SQL日志 18 | */ 19 | private Boolean enable; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/mybatis/utils/MybatisUtil.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.mybatis.utils; 2 | 3 | import cn.hutool.core.lang.Dict; 4 | import cn.tycoding.boot.common.core.api.QueryPage; 5 | import cn.tycoding.boot.common.mybatis.constant.MybatisConstant; 6 | import com.baomidou.mybatisplus.core.metadata.IPage; 7 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 8 | 9 | /** 10 | * Mybatis 工具类 11 | * 12 | * @author tycoding 13 | * @since 2021/5/21 14 | */ 15 | public class MybatisUtil { 16 | 17 | /** 18 | * 分页查询:格式化响应数据结构 19 | * 20 | * @param page 分页数据 21 | * @return 格式化后的Map对象 22 | */ 23 | public static Dict getData(IPage page) { 24 | return Dict.create().set(MybatisConstant.PAGE_ROWS, page.getRecords()) 25 | .set(MybatisConstant.PAGE_TOTAL, (int) page.getTotal()); 26 | } 27 | 28 | /** 29 | * QueryPage对象转换为Page对象 30 | */ 31 | public static IPage wrap(T t, QueryPage query) { 32 | return new Page(query.getPage(), query.getLimit()); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/oss/OssAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.oss; 2 | 3 | import cn.tycoding.boot.common.oss.props.LocalFileProperties; 4 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.core.annotation.Order; 7 | 8 | /** 9 | * Oss配置注入 10 | * 11 | * @author tycoding 12 | * @since 2021/5/21 13 | */ 14 | @Order 15 | @Configuration 16 | @EnableConfigurationProperties({LocalFileProperties.class}) 17 | public class OssAutoConfiguration { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/oss/props/LocalFileProperties.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.oss.props; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * 本地文件上传配置 8 | * 9 | * @author tycoding 10 | * @since 2021/6/3 11 | */ 12 | @Data 13 | @ConfigurationProperties("tumo-boot.file") 14 | public class LocalFileProperties { 15 | 16 | /** 17 | * 文件上传地址 18 | */ 19 | private String uploadPath = System.getProperty("user.dir") + "/target/classes/static/upload"; 20 | 21 | /** 22 | * 文件访问地址 23 | */ 24 | private String remotePath = "http://127.0.0.1:8090/upload"; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/oss/utils/OssUtil.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.oss.utils; 2 | 3 | import cn.hutool.core.date.DatePattern; 4 | import cn.hutool.core.date.DateUtil; 5 | import cn.hutool.core.io.FileUtil; 6 | import cn.hutool.core.util.URLUtil; 7 | import com.baomidou.mybatisplus.core.toolkit.IdWorker; 8 | 9 | import java.util.Date; 10 | 11 | /** 12 | * Oss工具类 13 | * 14 | * @author tycoding 15 | * @since 2021/6/3 16 | */ 17 | public class OssUtil { 18 | 19 | /** 20 | * 获取Bucket文件夹名称 21 | * 22 | * @return 根据日期格式化的文件夹名称 23 | */ 24 | public static String getBucket() { 25 | return "/" + DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN); 26 | } 27 | 28 | /** 29 | * 根据随机值组建文件名称 30 | * 31 | * @param originFile 原始文件名称 32 | * @return 通过雪花算法获取新文件名称 33 | */ 34 | public static String getName(String originFile) { 35 | String suffix = FileUtil.getSuffix(originFile); 36 | return IdWorker.getIdStr() + "." + suffix; 37 | } 38 | 39 | /** 40 | * 获取文件访问URL地址 41 | * 42 | * @param url 文件上传服务地址 43 | * @param bucket Bucket文件夹路径 44 | * @param fileName 新文件名称 45 | * @return 拼接后的文件访问地址 46 | */ 47 | public static String getUrl(String url, String bucket, String fileName) { 48 | if (url.endsWith("/")) { 49 | url = url.substring(0, url.length() - 1); 50 | } 51 | return URLUtil.normalize(url + bucket + "/" + fileName); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/redis/component/RedisComponent.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.redis.component; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.cache.annotation.EnableCaching; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.data.redis.connection.RedisConnectionFactory; 7 | import org.springframework.data.redis.core.RedisTemplate; 8 | import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; 9 | import org.springframework.data.redis.serializer.StringRedisSerializer; 10 | import org.springframework.stereotype.Component; 11 | 12 | /** 13 | * 自定义Redis配置 14 | * 15 | * @author tycoding 16 | * @since 2021/5/21 17 | */ 18 | @Component 19 | @EnableCaching 20 | @RequiredArgsConstructor 21 | public class RedisComponent { 22 | 23 | private final RedisConnectionFactory connectionFactory; 24 | 25 | @Bean(name = {"redisTemplate"}) 26 | public RedisTemplate redisTemplate() { 27 | RedisTemplate redisTemplate = new RedisTemplate<>(); 28 | redisTemplate.setKeySerializer(new StringRedisSerializer()); 29 | redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); 30 | redisTemplate.setConnectionFactory(connectionFactory); 31 | return redisTemplate; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/redis/utils/RedisUtil.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.redis.utils; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.data.redis.core.ConvertingCursor; 5 | import org.springframework.data.redis.core.Cursor; 6 | import org.springframework.data.redis.core.RedisTemplate; 7 | import org.springframework.data.redis.core.ScanOptions; 8 | import org.springframework.data.redis.serializer.RedisSerializer; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.UUID; 13 | 14 | /** 15 | * Redis工具类 16 | * 17 | * @author tycoding 18 | * @since 2021/5/21 19 | */ 20 | @Slf4j 21 | public class RedisUtil { 22 | 23 | /** 24 | * 获取随机Key 25 | * 26 | * @return 随机字符串值 27 | */ 28 | public static String getKey() { 29 | return UUID.randomUUID().toString(); 30 | } 31 | 32 | /** 33 | * 根据指定Key前缀分页查询Key列表 34 | * 35 | * @param redisTemplate RedisTemplate对象 36 | * @param patternKey 指定前缀规则的Key 37 | * @param page 页码 38 | * @param limit 每页多少条 39 | * @return 分页后的列表 40 | */ 41 | public static List getKeysPage(RedisTemplate redisTemplate, String patternKey, int page, int limit) { 42 | List list = new ArrayList<>(); 43 | ScanOptions options = ScanOptions.scanOptions().count(1000).match(patternKey).build(); 44 | RedisSerializer redisSerializer = redisTemplate.getKeySerializer(); 45 | 46 | Cursor cursor = (Cursor) redisTemplate.executeWithStickyConnection(redisConnection -> 47 | new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize)); 48 | 49 | if (cursor == null) { 50 | return list; 51 | } 52 | 53 | int flagIndex = 0; 54 | int startIndex = (page - 1) * limit; 55 | int endIndex = page * limit; 56 | while (cursor.hasNext()) { 57 | if (flagIndex >= startIndex && flagIndex < endIndex) { 58 | list.add(cursor.next().toString()); 59 | flagIndex++; 60 | continue; 61 | } 62 | if (flagIndex >= endIndex) { 63 | break; 64 | } 65 | flagIndex++; 66 | cursor.next(); 67 | } 68 | cursor.close(); 69 | return list; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/common/redis/utils/TokenInfo.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.common.redis.utils; 2 | 3 | import lombok.Data; 4 | import org.springframework.security.oauth2.common.OAuth2RefreshToken; 5 | 6 | import java.io.Serializable; 7 | import java.util.Date; 8 | import java.util.Set; 9 | 10 | /** 11 | * 对Redis中Token信息的扩展封装 12 | * 13 | * @author tycoding 14 | * @see org.springframework.security.oauth2.common.DefaultOAuth2AccessToken 15 | * @since 2021/6/12 16 | */ 17 | @Data 18 | public class TokenInfo implements Serializable { 19 | private static final long serialVersionUID = -1331900349298552602L; 20 | 21 | /** 22 | * Token值 23 | */ 24 | private String value; 25 | 26 | /** 27 | * 过期时间 28 | */ 29 | private Date expiration; 30 | 31 | /** 32 | * Token类型 33 | */ 34 | private String tokenType; 35 | 36 | /** 37 | * 刷新Token 38 | */ 39 | private OAuth2RefreshToken refreshToken; 40 | 41 | /** 42 | * Scope 43 | */ 44 | private Set scope; 45 | 46 | /** 47 | * 用户信息 48 | */ 49 | private Object principal; 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/auth/component/ResourceAuthExceptionEntryPoint.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.auth.component; 2 | 3 | import cn.hutool.core.util.URLUtil; 4 | import cn.tycoding.boot.common.log.utils.SpringContextHolder; 5 | import cn.tycoding.boot.common.core.api.HttpCode; 6 | import cn.tycoding.boot.common.core.api.R; 7 | import cn.tycoding.boot.common.core.constant.CommonConstant; 8 | import cn.tycoding.boot.common.log.event.LogEvent; 9 | import cn.tycoding.boot.common.log.utils.SysLogUtil; 10 | import cn.tycoding.boot.modules.system.entity.SysLog; 11 | import com.fasterxml.jackson.databind.ObjectMapper; 12 | import lombok.RequiredArgsConstructor; 13 | import lombok.extern.slf4j.Slf4j; 14 | import org.springframework.security.core.AuthenticationException; 15 | import org.springframework.security.web.AuthenticationEntryPoint; 16 | import org.springframework.stereotype.Component; 17 | 18 | import javax.servlet.ServletException; 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.servlet.http.HttpServletResponse; 21 | import java.io.IOException; 22 | import java.io.PrintWriter; 23 | 24 | /** 25 | * 自定义资源服务器未授权异常 26 | * 27 | * @author tycoding 28 | * @since 2021/5/21 29 | */ 30 | @Slf4j 31 | @Component 32 | @RequiredArgsConstructor 33 | public class ResourceAuthExceptionEntryPoint implements AuthenticationEntryPoint { 34 | 35 | private final ObjectMapper objectMapper; 36 | 37 | @Override 38 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { 39 | response.setStatus(HttpCode.UN_AUTHORIZED.getCode()); 40 | response.setCharacterEncoding(CommonConstant.UTF_8); 41 | response.setContentType(CommonConstant.CONTENT_TYPE); 42 | PrintWriter writer = response.getWriter(); 43 | R result = new R<>(); 44 | result.setCode(HttpCode.UN_AUTHORIZED.getCode()); 45 | result.setMsg(HttpCode.UN_AUTHORIZED.getMsg()); 46 | log.error(HttpCode.UN_AUTHORIZED.getMsg() + ", URL: {}", URLUtil.getPath(request.getRequestURI())); 47 | writer.append(objectMapper.writeValueAsString(result)); 48 | 49 | SysLog sysLog = SysLogUtil.build(SysLogUtil.TYPE_FAIL, HttpCode.UN_AUTHORIZED.getMsg(), null, null); 50 | SpringContextHolder.publishEvent(new LogEvent(sysLog)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/auth/component/TumoAuth2ExceptionSerializer.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.auth.component; 2 | 3 | import cn.tycoding.boot.modules.auth.exception.TumoAuth2Exception; 4 | import com.fasterxml.jackson.core.JsonGenerator; 5 | import com.fasterxml.jackson.databind.SerializerProvider; 6 | import com.fasterxml.jackson.databind.ser.std.StdSerializer; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * 自定义Oauth2异常响应格式 12 | * 13 | * @author tycoding 14 | * @see org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Serializer 15 | * @since 2021/5/21 16 | */ 17 | public class TumoAuth2ExceptionSerializer extends StdSerializer { 18 | private static final long serialVersionUID = 2241320471807860252L; 19 | 20 | protected TumoAuth2ExceptionSerializer() { 21 | super(TumoAuth2Exception.class); 22 | } 23 | 24 | @Override 25 | public void serialize(TumoAuth2Exception e, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { 26 | jsonGenerator.writeStartObject(); 27 | jsonGenerator.writeNumberField("code", e.getHttpErrorCode()); 28 | jsonGenerator.writeStringField("msg", e.getMessage()); 29 | jsonGenerator.writeStringField("data", null); 30 | jsonGenerator.writeEndObject(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/auth/component/TumoWebResponseExceptionTranslator.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.auth.component; 2 | 3 | import cn.tycoding.boot.common.core.api.HttpCode; 4 | import cn.tycoding.boot.modules.auth.exception.*; 5 | import org.springframework.http.HttpHeaders; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.security.access.AccessDeniedException; 9 | import org.springframework.security.core.AuthenticationException; 10 | import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer; 11 | import org.springframework.security.oauth2.common.exceptions.InsufficientScopeException; 12 | import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; 13 | import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator; 14 | import org.springframework.security.web.util.ThrowableAnalyzer; 15 | import org.springframework.web.HttpRequestMethodNotSupportedException; 16 | 17 | import java.io.IOException; 18 | 19 | /** 20 | * 重写Oauth2异常处理器 21 | * 22 | * @author tycoding 23 | * @since 2021/5/21 24 | */ 25 | public class TumoWebResponseExceptionTranslator implements WebResponseExceptionTranslator { 26 | 27 | private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer(); 28 | 29 | /** 30 | * @author tycoding 31 | * @see org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator 32 | * @since 2020/10/16 33 | */ 34 | @Override 35 | public ResponseEntity translate(Exception e) throws Exception { 36 | Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(e); 37 | 38 | // 请求未授权 39 | Exception ase = (AuthenticationException) this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain); 40 | if (ase != null) { 41 | return this.handleOAuth2Exception(new UnauthorizedException(e.getMessage(), e)); 42 | } 43 | 44 | // 拒绝访问异常 45 | ase = (AccessDeniedException) this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain); 46 | if (ase != null) { 47 | return this.handleOAuth2Exception(new ForbiddenException(ase.getMessage(), ase)); 48 | } 49 | 50 | // Token失效异常 51 | ase = (org.springframework.security.oauth2.common.exceptions.InvalidGrantException) throwableAnalyzer.getFirstThrowableOfType(org.springframework.security.oauth2.common.exceptions.InvalidGrantException.class, causeChain); 52 | if (ase != null) { 53 | return handleOAuth2Exception(new InvalidGrantException(ase.getMessage(), ase)); 54 | } 55 | 56 | // 请求方法不支持异常 57 | ase = (HttpRequestMethodNotSupportedException) throwableAnalyzer.getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain); 58 | if (ase != null) { 59 | return handleOAuth2Exception(new MethodNotAllowedException(ase.getMessage(), ase)); 60 | } 61 | 62 | // OAuth2Exception异常 63 | ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain); 64 | if (ase != null) { 65 | return this.handleOAuth2Exception((OAuth2Exception) ase); 66 | } 67 | 68 | return handleOAuth2Exception(new ServerErrorException(HttpCode.INTERNAL_SERVER_ERROR.getMsg(), e)); 69 | } 70 | 71 | private ResponseEntity handleOAuth2Exception(OAuth2Exception e) throws IOException { 72 | int code = e.getHttpErrorCode(); 73 | HttpHeaders headers = new HttpHeaders(); 74 | headers.set("Cache-Control", "no-store"); 75 | headers.set("Pragma", "no-cache"); 76 | if (code == HttpStatus.UNAUTHORIZED.value() || e instanceof InsufficientScopeException) { 77 | headers.set("WWW-Authenticate", String.format("%s %s", "Bearer", e.getSummary())); 78 | } 79 | TumoAuth2Exception auth2Exception = new TumoAuth2Exception(e.getMessage(), e.getCause(), code); 80 | return new ResponseEntity<>(auth2Exception, headers, HttpStatus.valueOf(code)); 81 | } 82 | 83 | private static class UnauthorizedException extends TumoAuth2Exception { 84 | 85 | public UnauthorizedException(String msg, Throwable t) { 86 | super(msg); 87 | } 88 | 89 | @Override 90 | public String getOAuth2ErrorCode() { 91 | return HttpCode.UN_AUTHORIZED.getMsg(); 92 | } 93 | 94 | @Override 95 | public int getHttpErrorCode() { 96 | return HttpCode.UN_AUTHORIZED.getCode(); 97 | } 98 | } 99 | 100 | private static class ForbiddenException extends TumoAuth2Exception { 101 | 102 | public ForbiddenException(String msg, Throwable t) { 103 | super(msg); 104 | } 105 | 106 | @Override 107 | public String getOAuth2ErrorCode() { 108 | return HttpCode.FORBIDDEN.getMsg(); 109 | } 110 | 111 | @Override 112 | public int getHttpErrorCode() { 113 | return HttpCode.FORBIDDEN.getCode(); 114 | } 115 | } 116 | 117 | private static class InvalidGrantException extends TumoAuth2Exception { 118 | 119 | public InvalidGrantException(String msg, Throwable e) { 120 | super(msg); 121 | } 122 | 123 | @Override 124 | public String getOAuth2ErrorCode() { 125 | return HttpCode.INVALID_GRANT.getMsg(); 126 | } 127 | 128 | @Override 129 | public int getHttpErrorCode() { 130 | return HttpCode.INVALID_GRANT.getCode(); 131 | } 132 | } 133 | 134 | private static class ServerErrorException extends TumoAuth2Exception { 135 | 136 | public ServerErrorException(String msg, Throwable t) { 137 | super(msg); 138 | } 139 | 140 | @Override 141 | public String getOAuth2ErrorCode() { 142 | return HttpCode.INTERNAL_SERVER_ERROR.getMsg(); 143 | } 144 | 145 | @Override 146 | public int getHttpErrorCode() { 147 | return HttpCode.INTERNAL_SERVER_ERROR.getCode(); 148 | } 149 | } 150 | 151 | private static class MethodNotAllowedException extends TumoAuth2Exception { 152 | 153 | public MethodNotAllowedException(String msg, Throwable e) { 154 | super(msg); 155 | } 156 | 157 | @Override 158 | public String getOAuth2ErrorCode() { 159 | return HttpCode.METHOD_NOT_SUPPORTED.getMsg(); 160 | } 161 | 162 | @Override 163 | public int getHttpErrorCode() { 164 | return HttpCode.METHOD_NOT_SUPPORTED.getCode(); 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/auth/config/AuthorizationServerConfig.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.auth.config; 2 | 3 | import cn.tycoding.boot.common.core.constant.ApiConstant; 4 | import cn.tycoding.boot.common.core.constant.CacheConstant; 5 | import cn.tycoding.boot.modules.auth.component.TumoWebResponseExceptionTranslator; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.data.redis.connection.RedisConnectionFactory; 10 | import org.springframework.http.HttpMethod; 11 | import org.springframework.security.authentication.AuthenticationManager; 12 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 13 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 14 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 15 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 16 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; 17 | import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService; 18 | import org.springframework.security.oauth2.provider.token.TokenStore; 19 | import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; 20 | 21 | import javax.sql.DataSource; 22 | 23 | /** 24 | * Oauth2 认证服务器配置(负责Token层面处理) 25 | * 26 | * @author tycoding 27 | * @since 2021/5/21 28 | */ 29 | @Configuration 30 | @RequiredArgsConstructor 31 | @EnableAuthorizationServer 32 | public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { 33 | 34 | private final DataSource dataSource; 35 | private final RedisConnectionFactory redisConnectionFactory; 36 | private final AuthenticationManager authenticationManager; 37 | 38 | @Bean 39 | public TokenStore tokenStore() { 40 | RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory); 41 | tokenStore.setPrefix(CacheConstant.OAUTH_PREFIX); 42 | return tokenStore; 43 | } 44 | 45 | @Override 46 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 47 | clients.withClientDetails(new JdbcClientDetailsService(dataSource)); 48 | } 49 | 50 | @Override 51 | public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 52 | endpoints 53 | .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) 54 | .pathMapping("/oauth/token", ApiConstant.API_OAUTH_TOKEN) 55 | .tokenStore(tokenStore()) 56 | .authenticationManager(authenticationManager) 57 | .exceptionTranslator(new TumoWebResponseExceptionTranslator()); 58 | } 59 | 60 | @Override 61 | public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { 62 | security 63 | .allowFormAuthenticationForClients() 64 | .checkTokenAccess("permitAll()"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/auth/config/ResourceServerConfig.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.auth.config; 2 | 3 | import cn.tycoding.boot.common.auth.props.AuthProperties; 4 | import cn.tycoding.boot.modules.auth.component.ResourceAuthExceptionEntryPoint; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 10 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; 11 | import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; 12 | 13 | /** 14 | * Oauth2 资源服务器配置(负责传递Token后接口层面处理) 15 | * 16 | * @author tycoding 17 | * @since 2021/5/21 18 | */ 19 | @Configuration 20 | @RequiredArgsConstructor 21 | @EnableResourceServer 22 | @EnableGlobalMethodSecurity(prePostEnabled = true) 23 | public class ResourceServerConfig extends ResourceServerConfigurerAdapter { 24 | private final AuthProperties authProperties; 25 | private final ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint; 26 | 27 | @Override 28 | public void configure(ResourceServerSecurityConfigurer resources) throws Exception { 29 | resources.authenticationEntryPoint(resourceAuthExceptionEntryPoint); 30 | } 31 | 32 | @Override 33 | public void configure(HttpSecurity http) throws Exception { 34 | http 35 | .authorizeRequests() 36 | .antMatchers(authProperties.getSkipUrl().toArray(new String[0])) 37 | .permitAll() 38 | 39 | .anyRequest() 40 | .authenticated() 41 | 42 | .and() 43 | .csrf().disable(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/auth/config/WebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.auth.config; 2 | 3 | import cn.tycoding.boot.common.auth.filter.CaptchaFilter; 4 | import cn.tycoding.boot.modules.auth.service.UserDetailsServiceImpl; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.security.authentication.AuthenticationManager; 9 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 12 | import org.springframework.security.core.userdetails.UserDetailsService; 13 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 14 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 15 | 16 | /** 17 | * 安全配置(配合Oauth2 授权服务器层面) 18 | * 19 | * @author tycoding 20 | * @since 2021/5/21 21 | */ 22 | @Configuration 23 | @RequiredArgsConstructor 24 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 25 | 26 | private final CaptchaFilter captchaFilter; 27 | 28 | @Bean 29 | public BCryptPasswordEncoder passwordEncoder() { 30 | return new BCryptPasswordEncoder(); 31 | } 32 | 33 | @Bean 34 | @Override 35 | protected AuthenticationManager authenticationManager() throws Exception { 36 | return super.authenticationManager(); 37 | } 38 | 39 | @Bean 40 | @Override 41 | protected UserDetailsService userDetailsService() { 42 | return new UserDetailsServiceImpl(); 43 | } 44 | 45 | @Override 46 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 47 | auth.userDetailsService(userDetailsService()); 48 | } 49 | 50 | @Override 51 | protected void configure(HttpSecurity http) throws Exception { 52 | http 53 | .logout() 54 | .deleteCookies("JSESSIONID") 55 | 56 | .and() 57 | .authorizeRequests() 58 | 59 | .anyRequest() 60 | .authenticated() 61 | 62 | .and() 63 | .csrf().disable(); 64 | 65 | http.addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/auth/dto/TumoUser.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.auth.dto; 2 | 3 | import lombok.Getter; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.core.userdetails.User; 6 | 7 | import java.util.Collection; 8 | 9 | /** 10 | * 扩展SpringSecurity User属性 11 | * 12 | * @author tycoding 13 | * @since 2021/5/21 14 | */ 15 | public class TumoUser extends User { 16 | private static final long serialVersionUID = -1462618142392426177L; 17 | 18 | @Getter 19 | private final Long id; 20 | 21 | @Getter 22 | private final Long deptId; 23 | 24 | public TumoUser(Long id, Long deptId, String username, String password, boolean enabled, 25 | boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, 26 | Collection authorities) { 27 | super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); 28 | this.id = id; 29 | this.deptId = deptId; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/auth/dto/UserInfo.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.auth.dto; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysDept; 4 | import cn.tycoding.boot.modules.upms.entity.SysRole; 5 | import cn.tycoding.boot.modules.upms.entity.SysUser; 6 | import lombok.Data; 7 | import lombok.experimental.Accessors; 8 | 9 | import java.io.Serializable; 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | /** 14 | * 自定义Oauth2 授权成功后存储的用户数据 15 | * 16 | * @author tycoding 17 | * @since 2021/5/21 18 | */ 19 | @Data 20 | @Accessors(chain = true) 21 | public class UserInfo implements Serializable { 22 | private static final long serialVersionUID = 547891924677981054L; 23 | 24 | /** 25 | * 用户基本信息 26 | */ 27 | private SysUser user; 28 | 29 | /** 30 | * 用户所属部门 31 | */ 32 | private SysDept dept; 33 | 34 | /** 35 | * 用户角色列表 36 | */ 37 | private List roles; 38 | 39 | /** 40 | * 用户权限标识 41 | */ 42 | private Set perms; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/auth/endpoint/AuthTokenEndpoint.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.auth.endpoint; 2 | 3 | import cn.hutool.captcha.CaptchaUtil; 4 | import cn.hutool.captcha.CircleCaptcha; 5 | import cn.hutool.core.lang.Dict; 6 | import cn.tycoding.boot.common.core.constant.ApiConstant; 7 | import cn.tycoding.boot.common.core.constant.CaptchaConstant; 8 | import cn.tycoding.boot.common.auth.utils.AuthUtil; 9 | import cn.tycoding.boot.common.core.api.QueryPage; 10 | import cn.tycoding.boot.common.core.api.R; 11 | import cn.tycoding.boot.common.core.constant.CacheConstant; 12 | import cn.tycoding.boot.common.core.utils.BeanUtil; 13 | import cn.tycoding.boot.common.mybatis.utils.MybatisUtil; 14 | import cn.tycoding.boot.common.redis.utils.RedisUtil; 15 | import cn.tycoding.boot.common.redis.utils.TokenInfo; 16 | import cn.tycoding.boot.modules.auth.dto.TumoUser; 17 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 18 | import lombok.RequiredArgsConstructor; 19 | import org.apache.commons.lang3.StringUtils; 20 | import org.springframework.cache.CacheManager; 21 | import org.springframework.data.redis.core.RedisTemplate; 22 | import org.springframework.http.HttpHeaders; 23 | import org.springframework.security.access.prepost.PreAuthorize; 24 | import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; 25 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 26 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 27 | import org.springframework.security.oauth2.provider.token.TokenStore; 28 | import org.springframework.web.bind.annotation.*; 29 | 30 | import java.time.Duration; 31 | import java.util.List; 32 | 33 | /** 34 | * 自定义授权相关的接口 35 | * 36 | * @author tycoding 37 | * @since 2021/5/21 38 | */ 39 | @RestController 40 | @RequiredArgsConstructor 41 | @RequestMapping(ApiConstant.API_AUTH_PREFIX) 42 | public class AuthTokenEndpoint { 43 | 44 | private final TokenStore tokenStore; 45 | private final RedisTemplate redisTemplate; 46 | private final CacheManager cacheManager; 47 | 48 | /** 49 | * 获取验证码 50 | */ 51 | @GetMapping("/captcha") 52 | public R getCaptcha() { 53 | CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(CaptchaConstant.CAPTCHA_WIDTH, CaptchaConstant.CAPTCHA_HEIGHT, CaptchaConstant.CAPTCHA_COUNT, CaptchaConstant.CAPTCHA_CIRCLE_COUNT); 54 | String code = captcha.getCode().toLowerCase(); 55 | String key = RedisUtil.getKey(); 56 | redisTemplate.opsForValue().set(CacheConstant.CAPTCHA_PREFIX + key, code, Duration.ofMinutes(CaptchaConstant.CAPTCHA_TIMEOUT)); 57 | return R.ok(Dict.create().set("key", key).set("image", captcha.getImageBase64())); 58 | } 59 | 60 | /** 61 | * 注销登录并清除Token 62 | */ 63 | @DeleteMapping("/logout") 64 | public R logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String token) { 65 | clear(AuthUtil.getToken(token)); 66 | return R.ok(); 67 | } 68 | 69 | private void clear(String token) { 70 | OAuth2AccessToken accessToken = tokenStore.readAccessToken(token); 71 | if (accessToken == null || StringUtils.isEmpty(accessToken.getValue())) { 72 | return; 73 | } 74 | OAuth2Authentication authentication = tokenStore.readAuthentication(accessToken); 75 | // 清空access_token 76 | tokenStore.removeAccessToken(accessToken); 77 | // 清空refresh_token 78 | tokenStore.removeRefreshToken(accessToken.getRefreshToken()); 79 | 80 | // 清空User Details 81 | cacheManager.getCache(CacheConstant.USER_DETAIL_KEY).evict(authentication.getName()); 82 | // 清空Menu Details 83 | TumoUser principal = (TumoUser) authentication.getPrincipal(); 84 | cacheManager.getCache(CacheConstant.MENU_DETAIL_KEY).evict(principal.getId()); 85 | } 86 | 87 | /** 88 | * 强制下线 89 | */ 90 | @DeleteMapping("/token/{token}") 91 | @PreAuthorize("@auth.hasAuth('system:token:delete')") 92 | public R tokenDel(@PathVariable String token) { 93 | clear(token); 94 | return R.ok(); 95 | } 96 | 97 | /** 98 | * 分页获取在线Token 99 | */ 100 | @GetMapping("/token/page") 101 | public R tokenPage(QueryPage queryPage) { 102 | String key = String.format("%sauth_to_access:*", CacheConstant.OAUTH_PREFIX); 103 | List keysPage = RedisUtil.getKeysPage(redisTemplate, key, 1, 10); 104 | List list = redisTemplate.opsForValue().multiGet(keysPage); 105 | List tokenInfoList = BeanUtil.copy(list, TokenInfo.class); 106 | 107 | tokenInfoList.forEach(info -> { 108 | OAuth2Authentication authentication = tokenStore.readAuthentication(info.getValue()); 109 | info.setPrincipal(authentication.getPrincipal()); 110 | }); 111 | 112 | int total = redisTemplate.keys(key).size(); 113 | Page page = new Page(queryPage.getPage(), queryPage.getLimit(), total); 114 | page.setRecords(tokenInfoList); 115 | return R.ok(MybatisUtil.getData(page)); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/auth/exception/TumoAuth2Exception.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.auth.exception; 2 | 3 | import cn.tycoding.boot.modules.auth.component.TumoAuth2ExceptionSerializer; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | import lombok.Getter; 6 | import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; 7 | 8 | /** 9 | * 重写 OAuth2 异常类 10 | * 11 | * @author tycoding 12 | * @since 2021/5/21 13 | */ 14 | @JsonSerialize(using = TumoAuth2ExceptionSerializer.class) 15 | public class TumoAuth2Exception extends OAuth2Exception { 16 | 17 | @Getter 18 | private int code; 19 | 20 | public TumoAuth2Exception(String msg) { 21 | super(msg); 22 | } 23 | 24 | public TumoAuth2Exception(String msg, int code) { 25 | super(msg); 26 | this.code = code; 27 | } 28 | 29 | public TumoAuth2Exception(String msg, Throwable t, int code) { 30 | super(msg, t); 31 | this.code = code; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/auth/service/UserDetailsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.auth.service; 2 | 3 | import cn.tycoding.boot.common.core.constant.AuthConstant; 4 | import cn.tycoding.boot.common.auth.utils.AuthUtil; 5 | import cn.tycoding.boot.common.core.constant.CacheConstant; 6 | import cn.tycoding.boot.modules.auth.dto.TumoUser; 7 | import cn.tycoding.boot.modules.auth.dto.UserInfo; 8 | import cn.tycoding.boot.modules.auth.exception.TumoAuth2Exception; 9 | import cn.tycoding.boot.modules.upms.entity.SysRole; 10 | import cn.tycoding.boot.modules.upms.service.SysUserService; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.cache.Cache; 14 | import org.springframework.cache.CacheManager; 15 | import org.springframework.security.core.GrantedAuthority; 16 | import org.springframework.security.core.authority.AuthorityUtils; 17 | import org.springframework.security.core.userdetails.UserDetails; 18 | import org.springframework.security.core.userdetails.UserDetailsService; 19 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 20 | import org.springframework.stereotype.Service; 21 | 22 | import java.util.HashSet; 23 | import java.util.List; 24 | import java.util.Set; 25 | 26 | /** 27 | * 封装授权时如何加载用户信息 28 | * 29 | * @author tycoding 30 | * @since 2021/5/21 31 | */ 32 | @Slf4j 33 | @Service 34 | public class UserDetailsServiceImpl implements UserDetailsService { 35 | 36 | @Autowired 37 | private SysUserService sysUserService; 38 | 39 | @Autowired 40 | private CacheManager cacheManager; 41 | 42 | /** 43 | * 加载用户信息,在这里可做登录用户的权限、角色判断 44 | * 45 | * @param username 用户名 46 | * @return UserDetails对象 47 | * @throws UsernameNotFoundException 用户名没有找到异常 48 | */ 49 | @Override 50 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 51 | Cache cache = cacheManager.getCache(CacheConstant.USER_DETAIL_KEY); 52 | if (cache != null && cache.get(username) != null) { 53 | UserInfo info = (UserInfo) cache.get(username).get(); 54 | return getUserDetails(info); 55 | } 56 | UserInfo info = sysUserService.info(username); 57 | return getUserDetails(info); 58 | } 59 | 60 | private UserDetails getUserDetails(UserInfo userInfo) { 61 | if (userInfo == null) { 62 | throw new UsernameNotFoundException("用户不存在"); 63 | } 64 | 65 | Set authSet = new HashSet<>(); 66 | 67 | List sysRoles = userInfo.getRoles(); 68 | if (sysRoles == null || sysRoles.size() == 0) { 69 | throw new TumoAuth2Exception(AuthUtil.NOT_ROLE_ERROR); 70 | } 71 | sysRoles.forEach(role -> authSet.add(AuthConstant.ROLE_PREFIX + role.getId() + AuthConstant.ROLE_SUFFIX + role.getAlias())); 72 | 73 | Set perms = userInfo.getPerms(); 74 | if (perms != null && perms.size() > 0) { 75 | authSet.addAll(perms); 76 | } 77 | 78 | List authorities = AuthorityUtils.createAuthorityList(authSet.toArray(new String[0])); 79 | 80 | return new TumoUser(userInfo.getUser().getId(), 81 | userInfo.getDept() == null ? null : userInfo.getDept().getId(), 82 | userInfo.getUser().getUsername(), 83 | userInfo.getUser().getPassword(), 84 | true, 85 | true, 86 | true, 87 | true, 88 | authorities); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/controller/OssFileController.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.controller; 2 | 3 | import cn.tycoding.boot.common.core.api.QueryPage; 4 | import cn.tycoding.boot.common.core.api.R; 5 | import cn.tycoding.boot.common.core.constant.ApiConstant; 6 | import cn.tycoding.boot.common.mybatis.utils.MybatisUtil; 7 | import cn.tycoding.boot.modules.system.entity.OssFile; 8 | import cn.tycoding.boot.modules.system.service.OssFileService; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.security.access.prepost.PreAuthorize; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * 资源文件表(OssFile)控制器 17 | * 18 | * @author tycoding 19 | * @since 2021/5/21 20 | */ 21 | @RestController 22 | @RequiredArgsConstructor 23 | @RequestMapping(ApiConstant.API_SYSTEM_PREFIX + "/oss") 24 | public class OssFileController { 25 | 26 | private final OssFileService ossFileService; 27 | 28 | @GetMapping("/page") 29 | public R list(OssFile ossFile, QueryPage queryPage) { 30 | return R.ok(MybatisUtil.getData(ossFileService.page(ossFile, queryPage))); 31 | } 32 | 33 | @GetMapping("/{id}") 34 | public R getById(@PathVariable Long id) { 35 | return R.ok(ossFileService.getById(id)); 36 | } 37 | 38 | @PostMapping 39 | @PreAuthorize("@auth.hasAuth('system:oss:add')") 40 | public R add(@RequestBody OssFile ossFile) { 41 | ossFileService.save(ossFile); 42 | return R.ok(); 43 | } 44 | 45 | @PostMapping("/put-list") 46 | @PreAuthorize("@auth.hasAuth('system:oss:add')") 47 | public R addList(@RequestBody List list) { 48 | ossFileService.saveBatch(list); 49 | return R.ok(); 50 | } 51 | 52 | @PutMapping 53 | @PreAuthorize("@auth.hasAuth('system:oss:update')") 54 | public R update(@RequestBody OssFile ossFile) { 55 | ossFileService.updateById(ossFile); 56 | return R.ok(); 57 | } 58 | 59 | @DeleteMapping("/{id}") 60 | @PreAuthorize("@auth.hasAuth('system:oss:delete')") 61 | public R delete(@PathVariable Long id) { 62 | ossFileService.delete(id); 63 | return R.ok(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/controller/SysDictController.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.controller; 2 | 3 | import cn.tycoding.boot.common.core.api.QueryPage; 4 | import cn.tycoding.boot.common.core.api.R; 5 | import cn.tycoding.boot.common.core.constant.ApiConstant; 6 | import cn.tycoding.boot.common.log.exception.ServiceException; 7 | import cn.tycoding.boot.common.mybatis.utils.MybatisUtil; 8 | import cn.tycoding.boot.modules.system.entity.SysDict; 9 | import cn.tycoding.boot.modules.system.service.SysDictService; 10 | import com.baomidou.mybatisplus.core.toolkit.Wrappers; 11 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 12 | import lombok.RequiredArgsConstructor; 13 | import org.apache.commons.lang3.StringUtils; 14 | import org.springframework.security.access.prepost.PreAuthorize; 15 | import org.springframework.web.bind.annotation.*; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * 字典表(SysDict)表控制层 21 | * 22 | * @author TyCoding 23 | * @since 2021-08-06 24 | */ 25 | @RestController 26 | @RequiredArgsConstructor 27 | @RequestMapping(ApiConstant.API_SYSTEM_PREFIX + "/dict") 28 | public class SysDictController { 29 | 30 | private final SysDictService sysDictService; 31 | 32 | @GetMapping("/list") 33 | public R> list(SysDict sysDict) { 34 | return R.ok(sysDictService.list()); 35 | } 36 | 37 | @GetMapping("/page") 38 | public R list(SysDict sysDict, QueryPage queryPage) { 39 | Page page = new Page<>(queryPage.getPage(), queryPage.getLimit()); 40 | return R.ok(MybatisUtil.getData(sysDictService.page(page, Wrappers.query().lambda() 41 | .like(StringUtils.isNotEmpty(sysDict.getName()), SysDict::getName, sysDict.getName()) 42 | .orderByDesc(SysDict::getSort)))); 43 | } 44 | 45 | @GetMapping("/{id}") 46 | public R findById(@PathVariable Long id) { 47 | return R.ok(sysDictService.getById(id)); 48 | } 49 | 50 | @PostMapping 51 | @PreAuthorize("@auth.hasAuth('system:dict:add')") 52 | public R add(@RequestBody SysDict sysDict) { 53 | sysDictService.save(sysDict); 54 | return R.ok(); 55 | } 56 | 57 | @PutMapping 58 | @PreAuthorize("@auth.hasAuth('system:dict:update')") 59 | public R update(@RequestBody SysDict sysDict) { 60 | sysDictService.updateById(sysDict); 61 | return R.ok(); 62 | } 63 | 64 | @DeleteMapping("/{id}") 65 | @PreAuthorize("@auth.hasAuth('system:dict:delete')") 66 | public R delete(@PathVariable Long id) { 67 | SysDict sysDict = sysDictService.getById(id); 68 | if (sysDict != null && sysDict.getIsSystem()) { 69 | throw new ServiceException("系统字典,不可删除"); 70 | } 71 | sysDictService.removeById(id); 72 | return R.ok(); 73 | } 74 | 75 | } 76 | 77 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/controller/SysDictItemController.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.controller; 2 | 3 | import cn.tycoding.boot.common.core.api.QueryPage; 4 | import cn.tycoding.boot.common.core.api.R; 5 | import cn.tycoding.boot.common.core.constant.ApiConstant; 6 | import cn.tycoding.boot.common.log.exception.ServiceException; 7 | import cn.tycoding.boot.common.mybatis.utils.MybatisUtil; 8 | import cn.tycoding.boot.modules.system.entity.SysDictItem; 9 | import cn.tycoding.boot.modules.system.service.SysDictItemService; 10 | import com.baomidou.mybatisplus.core.toolkit.Wrappers; 11 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 12 | import lombok.RequiredArgsConstructor; 13 | import org.apache.commons.lang3.StringUtils; 14 | import org.springframework.security.access.prepost.PreAuthorize; 15 | import org.springframework.web.bind.annotation.*; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * 字典项表(SysDictItem)表控制层 21 | * 22 | * @author TyCoding 23 | * @since 2021-08-07 13:58:33 24 | */ 25 | @RestController 26 | @RequiredArgsConstructor 27 | @RequestMapping(ApiConstant.API_SYSTEM_PREFIX + "/dict/item") 28 | public class SysDictItemController { 29 | 30 | private final SysDictItemService sysDictItemService; 31 | 32 | @GetMapping("/list") 33 | public R> list(SysDictItem sysDictItem) { 34 | return R.ok(sysDictItemService.list(Wrappers.query().lambda() 35 | .eq(StringUtils.isNotEmpty(sysDictItem.getType()), SysDictItem::getType, sysDictItem.getType()))); 36 | } 37 | 38 | @GetMapping("/page") 39 | public R list(@RequestParam Long dictId, QueryPage queryPage) { 40 | Page page = new Page<>(queryPage.getPage(), queryPage.getLimit()); 41 | return R.ok(MybatisUtil.getData(sysDictItemService.page(page, Wrappers.query().lambda() 42 | .eq(SysDictItem::getDictId, dictId) 43 | .orderByDesc(SysDictItem::getSort)))); 44 | } 45 | 46 | @GetMapping("/{id}") 47 | public R findById(@PathVariable Long id) { 48 | return R.ok(sysDictItemService.getById(id)); 49 | } 50 | 51 | @PostMapping 52 | @PreAuthorize("@auth.hasAuth('system:dict:item:add')") 53 | public R add(@RequestBody SysDictItem sysDictItem) { 54 | sysDictItemService.addDictItem(sysDictItem); 55 | return R.ok(); 56 | } 57 | 58 | @PutMapping 59 | @PreAuthorize("@auth.hasAuth('system:dict:item:update')") 60 | public R update(@RequestBody SysDictItem sysDictItem) { 61 | sysDictItemService.updateById(sysDictItem); 62 | return R.ok(); 63 | } 64 | 65 | @DeleteMapping("/{id}") 66 | @PreAuthorize("@auth.hasAuth('system:dict:item:delete')") 67 | public R delete(@PathVariable Long id) { 68 | SysDictItem sysDictItem = sysDictItemService.getById(id); 69 | if (sysDictItem != null && sysDictItem.getIsSystem()) { 70 | throw new ServiceException("系统字典,不可删除"); 71 | } 72 | sysDictItemService.removeById(id); 73 | return R.ok(); 74 | } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/controller/SysLogController.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.controller; 2 | 3 | import cn.hutool.core.lang.Dict; 4 | import cn.tycoding.boot.common.core.constant.ApiConstant; 5 | import cn.tycoding.boot.common.core.api.QueryPage; 6 | import cn.tycoding.boot.common.core.api.R; 7 | import cn.tycoding.boot.common.mybatis.utils.MybatisUtil; 8 | import cn.tycoding.boot.modules.system.entity.SysLog; 9 | import cn.tycoding.boot.modules.system.service.SysLogService; 10 | import lombok.RequiredArgsConstructor; 11 | import org.springframework.security.access.prepost.PreAuthorize; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | /** 15 | * 系统日志表(Log)表控制层 16 | * 17 | * @author tycoding 18 | * @since 2021/5/21 19 | */ 20 | @RestController 21 | @RequiredArgsConstructor 22 | @RequestMapping(ApiConstant.API_SYSTEM_PREFIX + "/log") 23 | public class SysLogController { 24 | 25 | private final SysLogService sysLogService; 26 | 27 | @GetMapping("/page") 28 | public R list(SysLog sysLog, QueryPage queryPage) { 29 | return R.ok(MybatisUtil.getData(sysLogService.list(sysLog, queryPage))); 30 | } 31 | 32 | @GetMapping("/{id}") 33 | public R findById(@PathVariable Long id) { 34 | return R.ok(sysLogService.getById(id)); 35 | } 36 | 37 | @DeleteMapping("/{id}") 38 | @PreAuthorize("@auth.hasAuth('system:log:delete')") 39 | public R delete(@PathVariable Long id) { 40 | sysLogService.delete(id); 41 | return R.ok(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/endpoint/OssEndpoint.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.endpoint; 2 | 3 | import cn.tycoding.boot.common.core.api.R; 4 | import cn.tycoding.boot.common.core.constant.ApiConstant; 5 | import cn.tycoding.boot.modules.system.service.OssService; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import org.springframework.web.multipart.MultipartFile; 11 | 12 | /** 13 | * 单独定义文件上传端点接口 14 | * 15 | * @author tycoding 16 | * @since 2021/5/25 17 | */ 18 | @RestController 19 | @RequiredArgsConstructor 20 | @RequestMapping(ApiConstant.API_BASE + "/oss") 21 | public class OssEndpoint { 22 | 23 | private final OssService ossService; 24 | 25 | @PostMapping("/put") 26 | public R put(MultipartFile file) { 27 | return R.ok(ossService.put(file)); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/entity/OssFile.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | 8 | import java.io.Serializable; 9 | import java.util.Date; 10 | 11 | /** 12 | * 资源文件表(OssFile)实体类 13 | * 14 | * @author tycoding 15 | * @since 2021/5/21 16 | */ 17 | @Data 18 | @TableName("oss_file") 19 | public class OssFile implements Serializable { 20 | private static final long serialVersionUID = -7225974227659141556L; 21 | 22 | /** 23 | * 主键 24 | */ 25 | @TableId(type = IdType.ASSIGN_ID) 26 | private Long id; 27 | 28 | /** 29 | * 文件原始名称 30 | */ 31 | private String originName; 32 | 33 | /** 34 | * 文件存储名称 35 | */ 36 | private String targetName; 37 | 38 | /** 39 | * 桶路径 40 | */ 41 | private String bucket; 42 | 43 | /** 44 | * 文件地址 45 | */ 46 | private String url; 47 | 48 | /** 49 | * 文件类型 50 | */ 51 | private String type; 52 | 53 | /** 54 | * 文件大小 55 | */ 56 | private Long size; 57 | 58 | /** 59 | * 文件描述 60 | */ 61 | private String des; 62 | 63 | /** 64 | * 创建时间 65 | */ 66 | private Date createTime; 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/entity/SysDict.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * 字典表(SysDict)实体类 12 | * 13 | * @author TyCoding 14 | * @since 2021-08-06 15 | */ 16 | @Data 17 | @TableName("sys_dict") 18 | public class SysDict implements Serializable { 19 | private static final long serialVersionUID = -66073261152467734L; 20 | 21 | /** 22 | * 主键 23 | */ 24 | @TableId(type = IdType.ASSIGN_ID) 25 | private Long id; 26 | 27 | /** 28 | * 类型 29 | */ 30 | private String type; 31 | 32 | /** 33 | * 名称 34 | */ 35 | private String name; 36 | 37 | /** 38 | * 描述 39 | */ 40 | private String des; 41 | 42 | /** 43 | * 排序 44 | */ 45 | private Integer sort; 46 | 47 | /** 48 | * 是否是系统内置 49 | */ 50 | private Boolean isSystem; 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/entity/SysDictItem.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * 字典项表(SysDictItem)实体类 12 | * 13 | * @author TyCoding 14 | * @since 2021-08-06 15 | */ 16 | @Data 17 | @TableName("sys_dict_item") 18 | public class SysDictItem implements Serializable { 19 | private static final long serialVersionUID = 859322231277654399L; 20 | 21 | /** 22 | * 主键 23 | */ 24 | @TableId(type = IdType.ASSIGN_ID) 25 | private Long id; 26 | 27 | /** 28 | * 字典表主键 29 | */ 30 | private Long dictId; 31 | 32 | /** 33 | * 字典值 34 | */ 35 | private String value; 36 | 37 | /** 38 | * 显示名称 39 | */ 40 | private String label; 41 | 42 | /** 43 | * 字典类型 44 | */ 45 | private String type; 46 | 47 | /** 48 | * 排序 49 | */ 50 | private Integer sort; 51 | 52 | /** 53 | * 描述 54 | */ 55 | private String des; 56 | 57 | /** 58 | * 是否是系统内置 59 | */ 60 | private Boolean isSystem; 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/entity/SysLog.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | import lombok.experimental.Accessors; 8 | 9 | import java.io.Serializable; 10 | import java.util.Date; 11 | 12 | /** 13 | * 系统日志表(Log)实体类 14 | * 15 | * @author tycoding 16 | * @since 2021/5/21 17 | */ 18 | @Data 19 | @TableName("sys_log") 20 | @Accessors(chain = true) 21 | public class SysLog implements Serializable { 22 | private static final long serialVersionUID = -39039111282732175L; 23 | 24 | /** 25 | * 主键 26 | */ 27 | @TableId(type = IdType.ASSIGN_ID) 28 | private Long id; 29 | 30 | /** 31 | * 操作用户 32 | */ 33 | private String username; 34 | 35 | /** 36 | * 日志类型 37 | */ 38 | private Integer type; 39 | 40 | /** 41 | * 操作描述 42 | */ 43 | private String operation; 44 | 45 | /** 46 | * 请求URL 47 | */ 48 | private String url; 49 | 50 | /** 51 | * 耗时(毫秒) 52 | */ 53 | private Long time; 54 | 55 | /** 56 | * 操作方法 57 | */ 58 | private String method; 59 | 60 | /** 61 | * 操作参数 62 | */ 63 | private String params; 64 | 65 | /** 66 | * IP地址 67 | */ 68 | private String ip; 69 | 70 | /** 71 | * 用户代理 72 | */ 73 | private String userAgent; 74 | 75 | /** 76 | * 操作时间 77 | */ 78 | private Date createTime; 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/mapper/OssFileMapper.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.mapper; 2 | 3 | import cn.tycoding.boot.modules.system.entity.OssFile; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * 资源文件表(OssFile)数据访问层 9 | * 10 | * @author tycoding 11 | * @since 2021/5/20 12 | */ 13 | @Mapper 14 | public interface OssFileMapper extends BaseMapper { 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/mapper/SysDictItemMapper.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.mapper; 2 | 3 | import cn.tycoding.boot.modules.system.entity.SysDictItem; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * 字典项表(SysDictItem)表数据库访问层 9 | * 10 | * @author TyCoding 11 | * @since 2021-08-06 12 | */ 13 | @Mapper 14 | public interface SysDictItemMapper extends BaseMapper { 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/mapper/SysDictMapper.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.mapper; 2 | 3 | import cn.tycoding.boot.modules.system.entity.SysDict; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * 字典表(SysDict)表数据库访问层 9 | * 10 | * @author TyCoding 11 | * @since 2021-08-06 12 | */ 13 | @Mapper 14 | public interface SysDictMapper extends BaseMapper { 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/mapper/SysLogMapper.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.mapper; 2 | 3 | import cn.tycoding.boot.modules.system.entity.SysLog; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * 系统日志表(Log)表数据库访问层 9 | * 10 | * @author tycoding 11 | * @since 2021/5/21 12 | */ 13 | @Mapper 14 | public interface SysLogMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/service/OssFileService.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.service; 2 | 3 | import cn.tycoding.boot.common.core.api.QueryPage; 4 | import cn.tycoding.boot.modules.system.entity.OssFile; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | /** 9 | * 资源文件表(OssFile)服务接口 10 | * 11 | * @author tycoding 12 | * @since 2021/5/20 13 | */ 14 | public interface OssFileService extends IService { 15 | 16 | /** 17 | * 分页查询 18 | */ 19 | IPage page(OssFile ossFile, QueryPage queryPage); 20 | 21 | /** 22 | * 删除文件 23 | */ 24 | void delete(Long id); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/service/OssService.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.service; 2 | 3 | import cn.tycoding.boot.modules.system.entity.OssFile; 4 | import org.springframework.web.multipart.MultipartFile; 5 | 6 | /** 7 | * @author tycoding 8 | * @since 2021/5/25 9 | */ 10 | public interface OssService { 11 | 12 | OssFile put(MultipartFile file); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/service/SysDictItemService.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.service; 2 | 3 | import cn.tycoding.boot.modules.system.entity.SysDictItem; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | * 字典项表(SysDictItem)表服务接口 8 | * 9 | * @author TyCoding 10 | * @since 2021-08-06 11 | */ 12 | public interface SysDictItemService extends IService { 13 | 14 | /** 15 | * 新增字典项 16 | * 17 | * @param sysDictItem 字典项信息 18 | */ 19 | void addDictItem(SysDictItem sysDictItem); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/service/SysDictService.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.service; 2 | 3 | import cn.tycoding.boot.modules.system.entity.SysDict; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | * 字典表(SysDict)表服务接口 8 | * 9 | * @author TyCoding 10 | * @since 2021-08-06 11 | */ 12 | public interface SysDictService extends IService { 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/service/SysLogService.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.service; 2 | 3 | import cn.tycoding.boot.common.core.api.QueryPage; 4 | import cn.tycoding.boot.modules.system.entity.SysLog; 5 | import com.baomidou.mybatisplus.core.metadata.IPage; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | /** 9 | * 系统日志表(Log)表服务接口 10 | * 11 | * @author tycoding 12 | * @since 2021/5/21 13 | */ 14 | public interface SysLogService extends IService { 15 | 16 | /** 17 | * 分页、条件查询 18 | */ 19 | IPage list(SysLog sysLog, QueryPage queryPage); 20 | 21 | /** 22 | * 新增 23 | */ 24 | void add(SysLog sysLog); 25 | /** 26 | * 删除 27 | */ 28 | void delete(Long id); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/service/impl/OssFileServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.service.impl; 2 | 3 | import cn.hutool.core.io.FileUtil; 4 | import cn.tycoding.boot.common.core.api.QueryPage; 5 | import cn.tycoding.boot.common.mybatis.utils.MybatisUtil; 6 | import cn.tycoding.boot.common.oss.props.LocalFileProperties; 7 | import cn.tycoding.boot.modules.system.entity.OssFile; 8 | import cn.tycoding.boot.modules.system.mapper.OssFileMapper; 9 | import cn.tycoding.boot.modules.system.service.OssFileService; 10 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 11 | import com.baomidou.mybatisplus.core.metadata.IPage; 12 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 13 | import lombok.RequiredArgsConstructor; 14 | import lombok.SneakyThrows; 15 | import org.apache.commons.lang3.StringUtils; 16 | import org.springframework.stereotype.Service; 17 | 18 | import java.io.File; 19 | 20 | /** 21 | * 资源文件表(OssFile)服务实现类 22 | * 23 | * @author tycoding 24 | * @since 2021/5/20 25 | */ 26 | @Service 27 | @RequiredArgsConstructor 28 | public class OssFileServiceImpl extends ServiceImpl implements OssFileService { 29 | 30 | private final LocalFileProperties properties; 31 | 32 | @Override 33 | public IPage page(OssFile ossFile, QueryPage queryPage) { 34 | return baseMapper.selectPage(MybatisUtil.wrap(ossFile, queryPage), new LambdaQueryWrapper() 35 | .like(StringUtils.isNotEmpty(ossFile.getOriginName()), OssFile::getOriginName, ossFile.getOriginName())); 36 | } 37 | 38 | @Override 39 | @SneakyThrows 40 | public void delete(Long id) { 41 | OssFile ossFile = baseMapper.selectById(id); 42 | if (ossFile != null) { 43 | // 删除数据库 44 | baseMapper.deleteById(id); 45 | // 删除磁盘文件 46 | String path = properties.getUploadPath() + ossFile.getBucket() + "/" + ossFile.getOriginName(); 47 | FileUtil.del(new File(path)); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/service/impl/OssServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.service.impl; 2 | 3 | import cn.hutool.core.io.FileUtil; 4 | import cn.tycoding.boot.common.log.exception.ServiceException; 5 | import cn.tycoding.boot.common.oss.props.LocalFileProperties; 6 | import cn.tycoding.boot.common.oss.utils.OssUtil; 7 | import cn.tycoding.boot.modules.system.entity.OssFile; 8 | import cn.tycoding.boot.modules.system.service.OssService; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.SneakyThrows; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.stereotype.Service; 13 | import org.springframework.web.multipart.MultipartFile; 14 | 15 | import java.io.File; 16 | import java.util.Date; 17 | 18 | /** 19 | * 文件上传服务类 20 | * 21 | * @author tycoding 22 | * @since 2021/5/25 23 | */ 24 | @Slf4j 25 | @Service 26 | @RequiredArgsConstructor 27 | public class OssServiceImpl implements OssService { 28 | 29 | private final LocalFileProperties properties; 30 | 31 | @Override 32 | public OssFile put(MultipartFile file) { 33 | if (file == null) { 34 | throw new ServiceException("文件列表为空"); 35 | } 36 | return transfer(file); 37 | } 38 | 39 | /** 40 | * 写入文件 41 | */ 42 | @SneakyThrows 43 | private OssFile transfer(MultipartFile file) { 44 | String bucket = OssUtil.getBucket(); 45 | // 目标上传地址 46 | String targetPath = properties.getUploadPath() + bucket; 47 | // 创建文件夹 48 | if (!FileUtil.isDirectory(targetPath)) { 49 | FileUtil.mkdir(targetPath); 50 | } 51 | // 目标文件名 52 | String targetName = OssUtil.getName(file.getOriginalFilename()); 53 | // 目标文件 54 | File targetFile = new File(targetPath, targetName); 55 | 56 | // 写入数据 57 | OssFile ossFile = new OssFile(); 58 | ossFile.setOriginName(file.getOriginalFilename()); 59 | ossFile.setTargetName(targetName); 60 | ossFile.setBucket(bucket); 61 | ossFile.setUrl(OssUtil.getUrl(properties.getRemotePath(), bucket, targetName)); 62 | ossFile.setSize(file.getSize()); 63 | ossFile.setType(FileUtil.getSuffix(targetName)); 64 | ossFile.setDes(file.getOriginalFilename()); 65 | ossFile.setCreateTime(new Date()); 66 | 67 | // 写入文件 68 | file.transferTo(targetFile); 69 | log.info("[OSS 文件写入成功,文件路径:{},访问地址:{}", targetFile.getPath(), ossFile.getUrl()); 70 | return ossFile; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/service/impl/SysDictItemServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.service.impl; 2 | 3 | import cn.tycoding.boot.modules.system.entity.SysDict; 4 | import cn.tycoding.boot.modules.system.entity.SysDictItem; 5 | import cn.tycoding.boot.modules.system.mapper.SysDictItemMapper; 6 | import cn.tycoding.boot.modules.system.mapper.SysDictMapper; 7 | import cn.tycoding.boot.modules.system.service.SysDictItemService; 8 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.stereotype.Service; 11 | 12 | /** 13 | * 字典项表(SysDictItem)表服务实现类 14 | * 15 | * @author TyCoding 16 | * @since 2021-08-06 17 | */ 18 | @Service 19 | @RequiredArgsConstructor 20 | public class SysDictItemServiceImpl extends ServiceImpl implements SysDictItemService { 21 | 22 | private final SysDictMapper sysDictMapper; 23 | private final SysDictItemMapper sysDictItemMapper; 24 | 25 | @Override 26 | public void addDictItem(SysDictItem sysDictItem) { 27 | SysDict sysDict = sysDictMapper.selectById(sysDictItem.getDictId()); 28 | if (sysDict != null) { 29 | sysDictItem.setDictId(sysDict.getId()); 30 | sysDictItem.setIsSystem(sysDict.getIsSystem()); 31 | 32 | sysDictItemMapper.insert(sysDictItem); 33 | } 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/service/impl/SysDictServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.service.impl; 2 | 3 | import cn.tycoding.boot.modules.system.entity.SysDict; 4 | import cn.tycoding.boot.modules.system.mapper.SysDictMapper; 5 | import cn.tycoding.boot.modules.system.service.SysDictService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.stereotype.Service; 9 | 10 | /** 11 | * 字典表(SysDict)表服务实现类 12 | * 13 | * @author TyCoding 14 | * @since 2021-08-06 15 | */ 16 | @Service 17 | @RequiredArgsConstructor 18 | public class SysDictServiceImpl extends ServiceImpl implements SysDictService { 19 | 20 | private final SysDictMapper sysDictMapper; 21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/system/service/impl/SysLogServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.system.service.impl; 2 | 3 | import cn.tycoding.boot.common.core.api.QueryPage; 4 | import cn.tycoding.boot.common.mybatis.utils.MybatisUtil; 5 | import cn.tycoding.boot.modules.system.entity.SysLog; 6 | import cn.tycoding.boot.modules.system.mapper.SysLogMapper; 7 | import cn.tycoding.boot.modules.system.service.SysLogService; 8 | import com.baomidou.mybatisplus.core.metadata.IPage; 9 | import com.baomidou.mybatisplus.core.toolkit.Wrappers; 10 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 11 | import lombok.RequiredArgsConstructor; 12 | import org.apache.commons.lang3.StringUtils; 13 | import org.springframework.stereotype.Service; 14 | import org.springframework.transaction.annotation.Transactional; 15 | 16 | /** 17 | * 系统日志表(Log)表服务实现类 18 | * 19 | * @author tycoding 20 | * @since 2021/5/21 21 | */ 22 | @Service 23 | @RequiredArgsConstructor 24 | public class SysLogServiceImpl extends ServiceImpl implements SysLogService { 25 | 26 | @Override 27 | public IPage list(SysLog sysLog, QueryPage queryPage) { 28 | return baseMapper.selectPage(MybatisUtil.wrap(sysLog, queryPage), 29 | Wrappers.lambdaQuery() 30 | .eq(sysLog.getType() != null, SysLog::getType, sysLog.getType()) 31 | .like(StringUtils.isNotEmpty(sysLog.getOperation()), SysLog::getOperation, sysLog.getOperation()) 32 | .orderByDesc(SysLog::getCreateTime) 33 | ); 34 | } 35 | 36 | @Override 37 | @Transactional(rollbackFor = Exception.class) 38 | public void add(SysLog sysLog) { 39 | baseMapper.insert(sysLog); 40 | } 41 | 42 | @Override 43 | @Transactional(rollbackFor = Exception.class) 44 | public void delete(Long id) { 45 | baseMapper.deleteById(id); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/controller/SysDeptController.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.controller; 2 | 3 | import cn.hutool.core.lang.tree.Tree; 4 | import cn.tycoding.boot.common.core.constant.ApiConstant; 5 | import cn.tycoding.boot.common.core.api.R; 6 | import cn.tycoding.boot.common.log.annotation.ApiLog; 7 | import cn.tycoding.boot.modules.upms.entity.SysDept; 8 | import cn.tycoding.boot.modules.upms.service.SysDeptService; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.security.access.prepost.PreAuthorize; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * 部门表(Dept)表控制层 17 | * 18 | * @author tycoding 19 | * @since 2021/5/21 20 | */ 21 | @RestController 22 | @RequiredArgsConstructor 23 | @RequestMapping(ApiConstant.API_UPMS_PREFIX + "/dept") 24 | public class SysDeptController { 25 | 26 | private final SysDeptService sysDeptService; 27 | 28 | @GetMapping("/list") 29 | public R> list(SysDept sysDept) { 30 | return R.ok(sysDeptService.list(sysDept)); 31 | } 32 | 33 | @GetMapping("/tree") 34 | public R>> tree(SysDept sysDept) { 35 | return R.ok(sysDeptService.tree(sysDept)); 36 | } 37 | 38 | @GetMapping("/{id}") 39 | public R findById(@PathVariable Long id) { 40 | return R.ok(sysDeptService.getById(id)); 41 | } 42 | 43 | @PostMapping 44 | @ApiLog("新增部门") 45 | @PreAuthorize("@auth.hasAuth('upms:dept:add')") 46 | public R add(@RequestBody SysDept sysDept) { 47 | sysDept.setParentId(sysDept.getParentId() == null ? 0L : sysDept.getParentId()); 48 | sysDeptService.save(sysDept); 49 | return R.ok(); 50 | } 51 | 52 | @PutMapping 53 | @ApiLog("修改部门") 54 | @PreAuthorize("@auth.hasAuth('upms:dept:update')") 55 | public R update(@RequestBody SysDept sysDept) { 56 | sysDept.setParentId(sysDept.getParentId() == null ? 0L : sysDept.getParentId()); 57 | sysDeptService.updateById(sysDept); 58 | return R.ok(); 59 | } 60 | 61 | @DeleteMapping("/{id}") 62 | @ApiLog("删除部门") 63 | @PreAuthorize("@auth.hasAuth('upms:dept:delete')") 64 | public R delete(@PathVariable Long id) { 65 | sysDeptService.delete(id); 66 | return R.ok(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/controller/SysMenuController.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.controller; 2 | 3 | import cn.tycoding.boot.common.auth.utils.AuthUtil; 4 | import cn.tycoding.boot.common.core.api.R; 5 | import cn.tycoding.boot.common.core.constant.ApiConstant; 6 | import cn.tycoding.boot.common.log.annotation.ApiLog; 7 | import cn.tycoding.boot.modules.upms.dto.MenuTree; 8 | import cn.tycoding.boot.modules.upms.entity.SysMenu; 9 | import cn.tycoding.boot.modules.upms.service.SysMenuService; 10 | import lombok.RequiredArgsConstructor; 11 | import org.springframework.security.access.prepost.PreAuthorize; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * 菜单表(Menu)表控制层 18 | * 19 | * @author tycoding 20 | * @since 2021/5/21 21 | */ 22 | @RestController 23 | @RequiredArgsConstructor 24 | @RequestMapping(ApiConstant.API_UPMS_PREFIX + "/menu") 25 | public class SysMenuController { 26 | 27 | private final SysMenuService sysMenuService; 28 | 29 | @GetMapping("/tree") 30 | public R>> tree(SysMenu sysMenu) { 31 | return R.ok(sysMenuService.tree(sysMenu)); 32 | } 33 | 34 | @GetMapping("/build") 35 | public R>> build() { 36 | return R.ok(sysMenuService.build(AuthUtil.getUserId())); 37 | } 38 | 39 | @GetMapping("/list") 40 | public R> list(SysMenu sysMenu) { 41 | return R.ok(sysMenuService.list(sysMenu)); 42 | } 43 | 44 | @GetMapping("/{id}") 45 | public R findById(@PathVariable Long id) { 46 | return R.ok(sysMenuService.getById(id)); 47 | } 48 | 49 | @PostMapping 50 | @ApiLog("新增菜单") 51 | @PreAuthorize("@auth.hasAuth('upms:menu:add')") 52 | public R add(@RequestBody SysMenu sysMenu) { 53 | sysMenuService.add(sysMenu); 54 | return R.ok(); 55 | } 56 | 57 | @PutMapping 58 | @ApiLog("修改菜单") 59 | @PreAuthorize("@auth.hasAuth('upms:menu:update')") 60 | public R update(@RequestBody SysMenu sysMenu) { 61 | sysMenuService.update(sysMenu); 62 | return R.ok(); 63 | } 64 | 65 | @DeleteMapping("/{id}") 66 | @ApiLog("删除菜单") 67 | @PreAuthorize("@auth.hasAuth('upms:menu:delete')") 68 | public R delete(@PathVariable Long id) { 69 | sysMenuService.delete(id); 70 | return R.ok(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/controller/SysRoleController.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.controller; 2 | 3 | import cn.hutool.core.lang.tree.Tree; 4 | import cn.tycoding.boot.common.auth.utils.AuthUtil; 5 | import cn.tycoding.boot.common.core.constant.ApiConstant; 6 | import cn.tycoding.boot.common.core.api.R; 7 | import cn.tycoding.boot.common.log.annotation.ApiLog; 8 | import cn.tycoding.boot.modules.upms.dto.SysRoleDTO; 9 | import cn.tycoding.boot.modules.upms.entity.SysRole; 10 | import cn.tycoding.boot.modules.upms.service.SysRoleService; 11 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 12 | import lombok.RequiredArgsConstructor; 13 | import org.springframework.security.access.prepost.PreAuthorize; 14 | import org.springframework.web.bind.annotation.*; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * 角色表(Role)表控制层 20 | * 21 | * @author tycoding 22 | * @since 2021/5/21 23 | */ 24 | @RestController 25 | @RequiredArgsConstructor 26 | @RequestMapping(ApiConstant.API_UPMS_PREFIX + "/role") 27 | public class SysRoleController { 28 | 29 | private final SysRoleService sysRoleService; 30 | 31 | @GetMapping("/list") 32 | public R> list(SysRole sysRole) { 33 | return R.ok(sysRoleService.list(new LambdaQueryWrapper() 34 | .ne(SysRole::getAlias, AuthUtil.ADMINISTRATOR) 35 | .eq(SysRole::getStatus, true))); 36 | } 37 | 38 | @GetMapping("/tree") 39 | public R>> tree(SysRole sysRole) { 40 | return R.ok(sysRoleService.tree(sysRole)); 41 | } 42 | 43 | @GetMapping("/{id}") 44 | public R findById(@PathVariable Long id) { 45 | return R.ok(sysRoleService.findById(id)); 46 | } 47 | 48 | @PostMapping 49 | @ApiLog("新增角色") 50 | @PreAuthorize("@auth.hasAuth('upms:role:add')") 51 | public R add(@RequestBody SysRoleDTO sysRole) { 52 | sysRoleService.add(sysRole); 53 | return R.ok(); 54 | } 55 | 56 | @PutMapping 57 | @ApiLog("修改角色") 58 | @PreAuthorize("@auth.hasAuth('upms:role:update')") 59 | public R update(@RequestBody SysRoleDTO sysRole) { 60 | sysRoleService.update(sysRole); 61 | return R.ok(); 62 | } 63 | 64 | @DeleteMapping("/{id}") 65 | @ApiLog("删除角色") 66 | @PreAuthorize("@auth.hasAuth('upms:role:delete')") 67 | public R delete(@PathVariable Long id) { 68 | sysRoleService.delete(id); 69 | return R.ok(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/controller/SysUserController.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.controller; 2 | 3 | import cn.hutool.core.lang.Dict; 4 | import cn.tycoding.boot.common.core.constant.ApiConstant; 5 | import cn.tycoding.boot.common.auth.utils.AuthUtil; 6 | import cn.tycoding.boot.common.core.api.QueryPage; 7 | import cn.tycoding.boot.common.core.api.R; 8 | import cn.tycoding.boot.common.log.annotation.ApiLog; 9 | import cn.tycoding.boot.common.mybatis.utils.MybatisUtil; 10 | import cn.tycoding.boot.modules.auth.dto.UserInfo; 11 | import cn.tycoding.boot.modules.upms.dto.SysUserDTO; 12 | import cn.tycoding.boot.modules.upms.entity.SysUser; 13 | import cn.tycoding.boot.modules.upms.service.SysUserService; 14 | import lombok.RequiredArgsConstructor; 15 | import org.springframework.security.access.prepost.PreAuthorize; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * 用户表(User)表控制层 22 | * 23 | * @author tycoding 24 | * @since 2021/5/21 25 | */ 26 | @RestController 27 | @RequiredArgsConstructor 28 | @RequestMapping(ApiConstant.API_UPMS_PREFIX + "/user") 29 | public class SysUserController { 30 | 31 | private final SysUserService sysUserService; 32 | 33 | @GetMapping("/info") 34 | public R info() { 35 | UserInfo userInfo = sysUserService.info(AuthUtil.getUsername()); 36 | userInfo.getUser().setPassword(null); 37 | return R.ok(userInfo); 38 | } 39 | 40 | @GetMapping("/checkName") 41 | public R checkName(SysUserDTO sysUser) { 42 | return R.ok(sysUserService.checkName(sysUser)); 43 | } 44 | 45 | @GetMapping("/list") 46 | public R> list(SysUser sysUser) { 47 | return R.ok(sysUserService.list(sysUser)); 48 | } 49 | 50 | @GetMapping("/page") 51 | public R page(SysUserDTO user, QueryPage queryPage) { 52 | return R.ok(MybatisUtil.getData(sysUserService.page(user, queryPage))); 53 | } 54 | 55 | @GetMapping("/{id}") 56 | public R findById(@PathVariable Long id) { 57 | return R.ok(sysUserService.findById(id)); 58 | } 59 | 60 | @PostMapping 61 | @ApiLog("新增用户") 62 | @PreAuthorize("@auth.hasAuth('upms:user:add')") 63 | public R add(@RequestBody SysUserDTO user) { 64 | sysUserService.add(user); 65 | return R.ok(); 66 | } 67 | 68 | @PutMapping 69 | @ApiLog("修改用户") 70 | @PreAuthorize("@auth.hasAuth('upms:user:update')") 71 | public R update(@RequestBody SysUserDTO user) { 72 | sysUserService.update(user); 73 | return R.ok(); 74 | } 75 | 76 | @DeleteMapping("/{id}") 77 | @ApiLog("删除用户") 78 | @PreAuthorize("@auth.hasAuth('upms:user:delete')") 79 | public R delete(@PathVariable Long id) { 80 | SysUser user = sysUserService.getById(id); 81 | if (user != null) { 82 | sysUserService.delete(id, user.getUsername()); 83 | } 84 | return R.ok(); 85 | } 86 | 87 | @GetMapping("/reset") 88 | @ApiLog("重置密码") 89 | @PreAuthorize("@auth.hasAuth('upms:user:reset')") 90 | public R reset(@RequestParam Long id, String password) { 91 | SysUser user = sysUserService.getById(id); 92 | if (user != null) { 93 | sysUserService.reset(id, password, user.getUsername()); 94 | } 95 | return R.ok(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/dto/MenuMeta.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 封装前端路由所需的路径名称、图标格式 10 | * 11 | * @author tycoding 12 | * @since 2021/5/21 13 | */ 14 | @Data 15 | @AllArgsConstructor 16 | public class MenuMeta implements Serializable { 17 | private static final long serialVersionUID = 547891924677981054L; 18 | 19 | /** 20 | * 标题 21 | */ 22 | private String title; 23 | 24 | /** 25 | * 图标名称 26 | */ 27 | private String icon; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/dto/MenuTree.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | import lombok.experimental.Accessors; 6 | 7 | import java.io.Serializable; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * 按照前端路由格式封装 13 | * 14 | * @author tycoding 15 | * @since 2021/5/21 16 | */ 17 | @Data 18 | @Accessors(chain = true) 19 | @JsonInclude(JsonInclude.Include.NON_EMPTY) 20 | public class MenuTree implements Serializable { 21 | private static final long serialVersionUID = 547891924677981054L; 22 | 23 | /** 24 | * 节点ID 25 | */ 26 | private Long id; 27 | 28 | /** 29 | * 父节点ID 30 | */ 31 | private Long parentId; 32 | 33 | /** 34 | * 路由名称 35 | */ 36 | private String name; 37 | 38 | /** 39 | * 路由地址 40 | */ 41 | private String path; 42 | 43 | /** 44 | * 类型 45 | */ 46 | private String type; 47 | 48 | /** 49 | * 组件路径 50 | */ 51 | private String component; 52 | 53 | /** 54 | * 权限标识 55 | */ 56 | private String perms; 57 | 58 | /** 59 | * 重定向地址 60 | */ 61 | private String redirect; 62 | 63 | /** 64 | * icon && title 信息 65 | */ 66 | private MenuMeta meta; 67 | 68 | /** 69 | * 排序 70 | */ 71 | private Integer orderNo; 72 | 73 | /** 74 | * 是否禁用 75 | */ 76 | private Boolean disabled; 77 | 78 | /** 79 | * 是否外链 80 | */ 81 | private Boolean isExt; 82 | 83 | /** 84 | * 是否缓存 85 | */ 86 | private Boolean keepalive; 87 | 88 | /** 89 | * 是否缓存 90 | */ 91 | private Boolean show; 92 | 93 | /** 94 | * 子节点 95 | */ 96 | private List> children = new ArrayList<>(); 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/dto/MenuTreeUtil.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.dto; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysMenu; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * 自定义封装Menu Tree 工具类 10 | * 11 | * @author tycoding 12 | * @since 2021/5/21 13 | */ 14 | public class MenuTreeUtil { 15 | 16 | private static List> init(List list) { 17 | List> treeList = new ArrayList<>(); 18 | list.forEach(menu -> { 19 | MenuTree tree = new MenuTree<>(); 20 | tree.setId(menu.getId()); 21 | tree.setParentId(menu.getParentId()); 22 | tree.setName(menu.getName()); 23 | tree.setPath(menu.getPath()); 24 | tree.setType(menu.getType()); 25 | tree.setComponent(menu.getComponent()); 26 | tree.setPerms(menu.getPerms()); 27 | tree.setMeta(new MenuMeta(menu.getName(), menu.getIcon())); 28 | tree.setOrderNo(menu.getOrderNo()); 29 | tree.setDisabled(menu.getIsDisabled()); 30 | tree.setIsExt(menu.getIsExt()); 31 | tree.setKeepalive(menu.getIsKeepalive()); 32 | tree.setShow(menu.getIsShow()); 33 | treeList.add(tree); 34 | }); 35 | return treeList; 36 | } 37 | 38 | 39 | public static List> build(List list) { 40 | List> nodes = init(list); 41 | List> tree = new ArrayList<>(); 42 | nodes.forEach(node -> { 43 | Long pid = node.getParentId(); 44 | if (pid == null || pid.equals(0L)) { 45 | // 父级节点 46 | if (node.getIsExt()) { 47 | node.setComponent("IFrame"); 48 | } 49 | tree.add(node); 50 | return; 51 | } 52 | for (MenuTree c : nodes) { 53 | Long id = c.getId(); 54 | if (id != null && id.equals(pid)) { 55 | c.getChildren().add(node); 56 | return; 57 | } 58 | } 59 | }); 60 | return tree; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/dto/SysRoleDTO.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.dto; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysRole; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * SysRole DTO封装 11 | * 12 | * @author tycoding 13 | * @since 2021/5/21 14 | */ 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | public class SysRoleDTO extends SysRole { 18 | private static final long serialVersionUID = -5792577217091151552L; 19 | 20 | /** 21 | * 菜单ID集合 22 | */ 23 | private List menuIds; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/dto/SysUserDTO.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.dto; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysRole; 4 | import cn.tycoding.boot.modules.upms.entity.SysUser; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * SysUser DTO封装 12 | * 13 | * @author tycoding 14 | * @since 2021/5/21 15 | */ 16 | @Data 17 | @EqualsAndHashCode(callSuper = true) 18 | public class SysUserDTO extends SysUser { 19 | private static final long serialVersionUID = 6173751370282753777L; 20 | 21 | /** 22 | * 部门名称 23 | */ 24 | private String deptName; 25 | 26 | /** 27 | * 角色信息 28 | */ 29 | private List roles; 30 | 31 | /** 32 | * 角色ID列表 33 | */ 34 | private List roleIds; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/entity/SysDept.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * 部门表(Dept)实体类 12 | * 13 | * @author tycoding 14 | * @since 2021/5/21 15 | */ 16 | @Data 17 | @TableName("sys_dept") 18 | public class SysDept implements Serializable { 19 | private static final long serialVersionUID = -94917153262781949L; 20 | 21 | /** 22 | * 主键 23 | */ 24 | @TableId(type = IdType.ASSIGN_ID) 25 | private Long id; 26 | 27 | /** 28 | * 上级部门ID 29 | */ 30 | private Long parentId; 31 | 32 | /** 33 | * 部门名称 34 | */ 35 | private String name; 36 | 37 | /** 38 | * 排序 39 | */ 40 | private Integer orderNo; 41 | 42 | /** 43 | * 描述 44 | */ 45 | private String des; 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/entity/SysMenu.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * 菜单表(Menu)实体类 12 | * 13 | * @author tycoding 14 | * @since 2021/5/21 15 | */ 16 | @Data 17 | @TableName("sys_menu") 18 | public class SysMenu implements Serializable { 19 | private static final long serialVersionUID = 427935315704878694L; 20 | 21 | /** 22 | * 主键 23 | */ 24 | @TableId(type = IdType.ASSIGN_ID) 25 | private Long id; 26 | 27 | /** 28 | * 资源名称 29 | */ 30 | private String name; 31 | 32 | /** 33 | * 父级ID 34 | */ 35 | private Long parentId; 36 | 37 | /** 38 | * 路由地址 39 | */ 40 | private String path; 41 | 42 | /** 43 | * 权限标识 44 | */ 45 | private String perms; 46 | 47 | /** 48 | * 菜单类型:如button按钮 menu菜单 49 | */ 50 | private String type; 51 | 52 | /** 53 | * 菜单图标 54 | */ 55 | private String icon; 56 | 57 | /** 58 | * 组件路径 59 | */ 60 | private String component; 61 | 62 | /** 63 | * 排序 64 | */ 65 | private Integer orderNo; 66 | 67 | /** 68 | * 是否禁用 69 | */ 70 | private Boolean isDisabled; 71 | 72 | /** 73 | * 是否外链 74 | */ 75 | private Boolean isExt; 76 | 77 | /** 78 | * 是否缓存 79 | */ 80 | private Boolean isKeepalive; 81 | 82 | /** 83 | * 是否缓存 84 | */ 85 | private Boolean isShow; 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/entity/SysRole.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * 角色表(Role)实体类 12 | * 13 | * @author tycoding 14 | * @since 2021/5/21 15 | */ 16 | @Data 17 | @TableName("sys_role") 18 | public class SysRole implements Serializable { 19 | private static final long serialVersionUID = 547891924677981054L; 20 | 21 | /** 22 | * 主键 23 | */ 24 | @TableId(type = IdType.ASSIGN_ID) 25 | private Long id; 26 | 27 | /** 28 | * 上级节点 29 | */ 30 | private Long parentId; 31 | 32 | /** 33 | * 角色名称 34 | */ 35 | private String name; 36 | 37 | /** 38 | * 角色别名 39 | */ 40 | private String alias; 41 | 42 | /** 43 | * 描述 44 | */ 45 | private String des; 46 | 47 | /** 48 | * 状态 49 | */ 50 | private Boolean status; 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/entity/SysRoleMenu.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.Data; 5 | import lombok.experimental.Accessors; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * 角色资源关联表(RoleMenu)实体类 11 | * 12 | * @author tycoding 13 | * @since 2021/5/21 14 | */ 15 | @Data 16 | @TableName("sys_role_menu") 17 | @Accessors(chain = true) 18 | public class SysRoleMenu implements Serializable { 19 | private static final long serialVersionUID = 854296054659457976L; 20 | 21 | /** 22 | * 角色ID 23 | */ 24 | private Long roleId; 25 | 26 | /** 27 | * 菜单ID 28 | */ 29 | private Long menuId; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/entity/SysUser.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | 8 | import java.io.Serializable; 9 | import java.util.Date; 10 | 11 | /** 12 | * 用户表(User)实体类 13 | * 14 | * @author tycoding 15 | * @since 2021/5/21 16 | */ 17 | @Data 18 | @TableName("sys_user") 19 | public class SysUser implements Serializable { 20 | private static final long serialVersionUID = -94827981963832107L; 21 | 22 | /** 23 | * 主键 24 | */ 25 | @TableId(type = IdType.ASSIGN_ID) 26 | private Long id; 27 | 28 | /** 29 | * 用户名 30 | */ 31 | private String username; 32 | 33 | /** 34 | * 密码 35 | */ 36 | private String password; 37 | 38 | /** 39 | * 真实姓名 40 | */ 41 | private String realName; 42 | 43 | /** 44 | * 性别 45 | */ 46 | private String sex; 47 | 48 | /** 49 | * 邮箱 50 | */ 51 | private String email; 52 | 53 | /** 54 | * 部门ID 55 | */ 56 | private Long deptId; 57 | 58 | /** 59 | * 头像 60 | */ 61 | private String avatar; 62 | 63 | /** 64 | * 手机 65 | */ 66 | private String phone; 67 | 68 | /** 69 | * 状态 0锁定 1有效 70 | */ 71 | private Boolean status; 72 | 73 | /** 74 | * 创建时间 75 | */ 76 | private Date createTime; 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/entity/SysUserRole.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import lombok.Data; 5 | import lombok.experimental.Accessors; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * 用户角色关联表(UserRole)实体类 11 | * 12 | * @author tycoding 13 | * @since 2021/5/21 14 | */ 15 | @Data 16 | @TableName("sys_user_role") 17 | @Accessors(chain = true) 18 | public class SysUserRole implements Serializable { 19 | private static final long serialVersionUID = -24775118196826771L; 20 | 21 | /** 22 | * 用户ID 23 | */ 24 | private Long userId; 25 | 26 | /** 27 | * 角色ID 28 | */ 29 | private Long roleId; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/mapper/SysDeptMapper.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.mapper; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysDept; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * 部门表(Dept)表数据库访问层 9 | * 10 | * @author tycoding 11 | * @since 2021/5/21 12 | */ 13 | @Mapper 14 | public interface SysDeptMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/mapper/SysMenuMapper.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.mapper; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysMenu; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Param; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 菜单表(Menu)表数据库访问层 12 | * 13 | * @author tycoding 14 | * @since 2021/5/21 15 | */ 16 | @Mapper 17 | public interface SysMenuMapper extends BaseMapper { 18 | 19 | List build(@Param("roleIds") List roleIds, @Param("type") String type); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/mapper/SysRoleMapper.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.mapper; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysRole; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @author tycoding 9 | * @since 2021/5/21 10 | */ 11 | @Mapper 12 | public interface SysRoleMapper extends BaseMapper { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/mapper/SysRoleMenuMapper.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.mapper; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysRoleMenu; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * 角色资源关联表(RoleMenu)表数据库访问层 9 | * 10 | * @author tycoding 11 | * @since 2021/5/21 12 | */ 13 | @Mapper 14 | public interface SysRoleMenuMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/mapper/SysUserMapper.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.mapper; 2 | 3 | import cn.tycoding.boot.modules.upms.dto.SysUserDTO; 4 | import cn.tycoding.boot.modules.upms.entity.SysUser; 5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 6 | import com.baomidou.mybatisplus.core.metadata.IPage; 7 | import org.apache.ibatis.annotations.Mapper; 8 | 9 | /** 10 | * 用户表(User)表数据库访问层 11 | * 12 | * @author tycoding 13 | * @since 2021/5/21 14 | */ 15 | @Mapper 16 | public interface SysUserMapper extends BaseMapper { 17 | 18 | IPage page(IPage page, SysUserDTO user, Long ignoreId, String ignoreName); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/mapper/SysUserRoleMapper.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.mapper; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysRole; 4 | import cn.tycoding.boot.modules.upms.entity.SysUser; 5 | import cn.tycoding.boot.modules.upms.entity.SysUserRole; 6 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 7 | import org.apache.ibatis.annotations.Mapper; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 用户角色关联表(UserRole)表数据库访问层 13 | * 14 | * @author tycoding 15 | * @since 2021/5/21 16 | */ 17 | @Mapper 18 | public interface SysUserRoleMapper extends BaseMapper { 19 | 20 | /** 21 | * 根据RoleID查询User集合 22 | */ 23 | List getUserListByRoleId(Long roleId); 24 | 25 | /** 26 | * 根据UserID查询Role集合 27 | */ 28 | List getRoleListByUserId(Long userId); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/service/SysDeptService.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.service; 2 | 3 | import cn.hutool.core.lang.tree.Tree; 4 | import cn.tycoding.boot.modules.upms.entity.SysDept; 5 | import com.baomidou.mybatisplus.extension.service.IService; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 部门表(Dept)表服务接口 11 | * 12 | * @author tycoding 13 | * @since 2021/5/21 14 | */ 15 | public interface SysDeptService extends IService { 16 | 17 | /** 18 | * 条件查询 19 | */ 20 | List list(SysDept sysDept); 21 | 22 | /** 23 | * 获取部门Tree 24 | */ 25 | List> tree(SysDept sysDept); 26 | 27 | /** 28 | * 删除 29 | */ 30 | void delete(Long id); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/service/SysMenuService.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.service; 2 | 3 | import cn.tycoding.boot.modules.upms.dto.MenuTree; 4 | import cn.tycoding.boot.modules.upms.entity.SysMenu; 5 | import cn.tycoding.boot.modules.upms.entity.SysRole; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 菜单表(Menu)表服务接口 12 | * 13 | * @author tycoding 14 | * @since 2021/5/21 15 | */ 16 | public interface SysMenuService extends IService { 17 | 18 | /** 19 | * 构建菜单Tree树 20 | */ 21 | List> tree(SysMenu sysMenu); 22 | 23 | /** 24 | * 构建左侧权限菜单 25 | */ 26 | List> build(Long userId); 27 | 28 | /** 29 | * 根据用户ID查询权限信息 30 | */ 31 | List getUserMenuList(List sysRoleList); 32 | 33 | /** 34 | * 条件查询 35 | */ 36 | List list(SysMenu sysMenu); 37 | 38 | /** 39 | * 新增 40 | */ 41 | void add(SysMenu sysMenu); 42 | 43 | /** 44 | * 修改 45 | */ 46 | void update(SysMenu sysMenu); 47 | 48 | /** 49 | * 删除 50 | */ 51 | void delete(Long id); 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/service/SysRoleMenuService.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.service; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysRoleMenu; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | * 角色资源关联表(RoleMenu)表服务接口 8 | * 9 | * @author tycoding 10 | * @since 2021/5/21 11 | */ 12 | public interface SysRoleMenuService extends IService { 13 | 14 | /** 15 | * 根据角色ID删除该角色的权限关联信息 16 | */ 17 | void deleteRoleMenusByRoleId(Long roleId); 18 | 19 | /** 20 | * 根据权限ID删除角色权限关联信息 21 | */ 22 | void deleteRoleMenusByMenuId(Long menuId); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/service/SysRoleService.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.service; 2 | 3 | import cn.hutool.core.lang.tree.Tree; 4 | import cn.tycoding.boot.modules.upms.dto.SysRoleDTO; 5 | import cn.tycoding.boot.modules.upms.entity.SysRole; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 角色表(Role)表服务接口 12 | * 13 | * @author tycoding 14 | * @since 2021/5/21 15 | */ 16 | public interface SysRoleService extends IService { 17 | 18 | /** 19 | * 根据用户ID查询其关联的所有角色 20 | */ 21 | List findRolesByUserId(Long id); 22 | 23 | /** 24 | * 获取角色Tree集合 25 | */ 26 | List> tree(SysRole sysRole); 27 | 28 | /** 29 | * 根据ID查询 30 | */ 31 | SysRoleDTO findById(Long roleId); 32 | 33 | /** 34 | * 新增角色 35 | */ 36 | void add(SysRoleDTO sysRole); 37 | 38 | /** 39 | * 修改角色 40 | */ 41 | void update(SysRoleDTO sysRole); 42 | 43 | /** 44 | * 删除 45 | */ 46 | void delete(Long id); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/service/SysUserRoleService.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.service; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysRole; 4 | import cn.tycoding.boot.modules.upms.entity.SysUser; 5 | import cn.tycoding.boot.modules.upms.entity.SysUserRole; 6 | import com.baomidou.mybatisplus.extension.service.IService; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 用户角色关联表(UserRole)表服务接口 12 | * 13 | * @author tycoding 14 | * @since 2021/5/21 15 | */ 16 | public interface SysUserRoleService extends IService { 17 | 18 | /** 19 | * 根据RoleID查询User集合 20 | */ 21 | List getUserListByRoleId(Long roleId); 22 | 23 | /** 24 | * 根据UserID查询Role集合 25 | */ 26 | List getRoleListByUserId(Long userId); 27 | 28 | /** 29 | * 根据用户ID删除该用户的角色关联信息 30 | */ 31 | void deleteUserRolesByUserId(Long userId); 32 | 33 | /** 34 | * 根据角色ID删除该用户的角色关联信息 35 | */ 36 | void deleteUserRolesByRoleId(Long roleId); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/service/SysUserService.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.service; 2 | 3 | import cn.tycoding.boot.common.core.api.QueryPage; 4 | import cn.tycoding.boot.modules.auth.dto.UserInfo; 5 | import cn.tycoding.boot.modules.upms.dto.SysUserDTO; 6 | import cn.tycoding.boot.modules.upms.entity.SysUser; 7 | import com.baomidou.mybatisplus.core.metadata.IPage; 8 | import com.baomidou.mybatisplus.extension.service.IService; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * 用户表(User)表服务接口 14 | * 15 | * @author tycoding 16 | * @since 2021/5/21 17 | */ 18 | public interface SysUserService extends IService { 19 | 20 | /** 21 | * 根据用户名查询 22 | */ 23 | SysUser findByName(String username); 24 | 25 | /** 26 | * 根据ID查询 27 | */ 28 | SysUserDTO findById(Long userId); 29 | 30 | /** 31 | * 根据用户名封装:用户信息、角色、部门、权限 32 | */ 33 | UserInfo info(String username); 34 | 35 | /** 36 | * 条件查询 37 | */ 38 | List list(SysUser sysUser); 39 | 40 | /** 41 | * 分页、条件查询 42 | */ 43 | IPage page(SysUserDTO user, QueryPage queryPage); 44 | 45 | /** 46 | * 校验用户名是否存在 47 | */ 48 | boolean checkName(SysUserDTO sysUser); 49 | 50 | /** 51 | * 新增 52 | */ 53 | void add(SysUserDTO user); 54 | 55 | /** 56 | * 修改 57 | */ 58 | void update(SysUserDTO user); 59 | 60 | /** 61 | * 删除 62 | */ 63 | void delete(Long id, String username); 64 | 65 | /** 66 | * 重置密码 67 | */ 68 | void reset(Long id, String password, String username); 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/service/impl/SysDeptServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.service.impl; 2 | 3 | import cn.hutool.core.collection.CollUtil; 4 | import cn.hutool.core.lang.Dict; 5 | import cn.hutool.core.lang.tree.Tree; 6 | import cn.hutool.core.lang.tree.TreeNode; 7 | import cn.hutool.core.lang.tree.TreeUtil; 8 | import cn.tycoding.boot.common.log.exception.ServiceException; 9 | import cn.tycoding.boot.modules.upms.entity.SysDept; 10 | import cn.tycoding.boot.modules.upms.mapper.SysDeptMapper; 11 | import cn.tycoding.boot.modules.upms.service.SysDeptService; 12 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 13 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 14 | import lombok.RequiredArgsConstructor; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.transaction.annotation.Transactional; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * 部门表(Dept)表服务实现类 22 | * 23 | * @author tycoding 24 | * @since 2021/5/21 25 | */ 26 | @Service 27 | @RequiredArgsConstructor 28 | public class SysDeptServiceImpl extends ServiceImpl implements SysDeptService { 29 | 30 | @Override 31 | public List list(SysDept sysDept) { 32 | return baseMapper.selectList(new LambdaQueryWrapper() 33 | .orderByAsc(SysDept::getOrderNo)); 34 | } 35 | 36 | @Override 37 | public List> tree(SysDept sysDept) { 38 | List sysDeptList = baseMapper.selectList(new LambdaQueryWrapper() 39 | .ne(sysDept.getId() != null, SysDept::getId, sysDept.getId())); 40 | 41 | // 构建树形结构 42 | List> nodeList = CollUtil.newArrayList(); 43 | sysDeptList.forEach(t -> { 44 | TreeNode node = new TreeNode<>( 45 | t.getId(), 46 | t.getParentId(), 47 | t.getName(), 48 | 0 49 | ); 50 | node.setExtra(Dict.create().set("orderNo", t.getOrderNo()).set("des", t.getDes())); 51 | nodeList.add(node); 52 | }); 53 | return TreeUtil.build(nodeList, 0L); 54 | } 55 | 56 | @Override 57 | @Transactional(rollbackFor = Exception.class) 58 | public void delete(Long id) { 59 | List list = baseMapper.selectList(new LambdaQueryWrapper().eq(SysDept::getParentId, id)); 60 | if (list.size() > 0) { 61 | throw new ServiceException("该部门包含子节点,不能删除"); 62 | } 63 | baseMapper.deleteById(id); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/service/impl/SysMenuServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.service.impl; 2 | 3 | import cn.tycoding.boot.common.auth.utils.AuthUtil; 4 | import cn.tycoding.boot.common.core.constant.CacheConstant; 5 | import cn.tycoding.boot.common.core.constant.CommonConstant; 6 | import cn.tycoding.boot.modules.auth.exception.TumoAuth2Exception; 7 | import cn.tycoding.boot.modules.upms.dto.MenuTreeUtil; 8 | import cn.tycoding.boot.common.log.exception.ServiceException; 9 | import cn.tycoding.boot.modules.upms.dto.MenuTree; 10 | import cn.tycoding.boot.modules.upms.entity.SysMenu; 11 | import cn.tycoding.boot.modules.upms.entity.SysRole; 12 | import cn.tycoding.boot.modules.upms.mapper.SysMenuMapper; 13 | import cn.tycoding.boot.modules.upms.service.SysMenuService; 14 | import cn.tycoding.boot.modules.upms.service.SysRoleMenuService; 15 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 16 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 17 | import lombok.RequiredArgsConstructor; 18 | import org.springframework.cache.annotation.CacheEvict; 19 | import org.springframework.cache.annotation.Cacheable; 20 | import org.springframework.stereotype.Service; 21 | import org.springframework.transaction.annotation.Transactional; 22 | 23 | import java.util.List; 24 | import java.util.stream.Collectors; 25 | 26 | /** 27 | * 菜单表(Menu)表服务实现类 28 | * 29 | * @author tycoding 30 | * @since 2021/5/21 31 | */ 32 | @Service 33 | @RequiredArgsConstructor 34 | public class SysMenuServiceImpl extends ServiceImpl implements SysMenuService { 35 | 36 | private final SysRoleMenuService sysRoleMenuService; 37 | 38 | @Override 39 | public List> tree(SysMenu sysMenu) { 40 | List list = baseMapper.selectList(new LambdaQueryWrapper() 41 | .ne(sysMenu.getId() != null, SysMenu::getId, sysMenu.getId()) 42 | .eq(sysMenu.getIsDisabled() != null, SysMenu::getIsDisabled, sysMenu.getIsDisabled()) 43 | ); 44 | return MenuTreeUtil.build(list); 45 | } 46 | 47 | @Override 48 | @Cacheable(value = CacheConstant.MENU_DETAIL_KEY, key = "#userId") 49 | public List> build(Long userId) { 50 | List roleIds = AuthUtil.getRoleIds(); 51 | if (roleIds.size() == 0) { 52 | throw new TumoAuth2Exception(AuthUtil.NOT_ROLE_ERROR); 53 | } 54 | if (AuthUtil.getRoleNames().contains(AuthUtil.ADMINISTRATOR)) { 55 | // 超级管理员,不做权限过滤 56 | roleIds.clear(); 57 | } 58 | List sysMenuList = baseMapper.build(roleIds, CommonConstant.MENU_TYPE_MENU); 59 | return MenuTreeUtil.build(sysMenuList); 60 | } 61 | 62 | @Override 63 | public List getUserMenuList(List sysRoleList) { 64 | List roleIds = sysRoleList.stream().map(SysRole::getId).collect(Collectors.toList()); 65 | return baseMapper.build(roleIds, null); 66 | } 67 | 68 | @Override 69 | public List list(SysMenu sysMenu) { 70 | return baseMapper.selectList(new LambdaQueryWrapper() 71 | .like(sysMenu.getName() != null, SysMenu::getName, sysMenu.getName()) 72 | .eq(sysMenu.getIsDisabled() != null, SysMenu::getIsDisabled, sysMenu.getIsDisabled()) 73 | .orderByAsc(SysMenu::getOrderNo)); 74 | } 75 | 76 | @Override 77 | @Transactional(rollbackFor = Exception.class) 78 | @CacheEvict(value = CacheConstant.MENU_DETAIL_KEY, allEntries = true) 79 | public void add(SysMenu sysMenu) { 80 | this.format(sysMenu); 81 | baseMapper.insert(sysMenu); 82 | } 83 | 84 | private void format(SysMenu sysMenu) { 85 | if (CommonConstant.MENU_TYPE_MENU.equals(sysMenu.getType())) { 86 | if (sysMenu.getPath() == null) { 87 | throw new ServiceException("[path]参数不能为空"); 88 | } 89 | if (sysMenu.getIcon() == null) { 90 | sysMenu.setIcon(CommonConstant.MENU_ICON); 91 | } 92 | if (sysMenu.getParentId() == null || sysMenu.getParentId() == 0) { 93 | // 父级节点 94 | if (sysMenu.getComponent() == null) { 95 | sysMenu.setComponent("Layout"); 96 | } 97 | if (!sysMenu.getPath().toLowerCase().startsWith("http") && !sysMenu.getPath().startsWith("/")) { 98 | sysMenu.setPath("/" + sysMenu.getPath()); 99 | } 100 | } else { 101 | // 子节点 102 | if (sysMenu.getPath().startsWith("/")) { 103 | sysMenu.setPath(sysMenu.getPath().substring(1)); 104 | } 105 | if (!sysMenu.getIsExt() && !sysMenu.getComponent().startsWith("/")) { 106 | sysMenu.setComponent("/" + sysMenu.getComponent()); 107 | } 108 | } 109 | } 110 | if (CommonConstant.MENU_TYPE_BUTTON.equals(sysMenu.getType())) { 111 | sysMenu.setPath(null); 112 | sysMenu.setIcon(null); 113 | sysMenu.setComponent(null); 114 | } 115 | } 116 | 117 | @Override 118 | @Transactional(rollbackFor = Exception.class) 119 | @CacheEvict(value = CacheConstant.MENU_DETAIL_KEY, allEntries = true) 120 | public void update(SysMenu sysMenu) { 121 | this.format(sysMenu); 122 | baseMapper.updateById(sysMenu); 123 | } 124 | 125 | @Override 126 | @Transactional(rollbackFor = Exception.class) 127 | @CacheEvict(value = CacheConstant.MENU_DETAIL_KEY, allEntries = true) 128 | public void delete(Long id) { 129 | List list = baseMapper.selectList(new LambdaQueryWrapper().eq(SysMenu::getParentId, id)); 130 | if (list.size() > 0) { 131 | throw new ServiceException("该菜单包含子节点,不能删除"); 132 | } 133 | sysRoleMenuService.deleteRoleMenusByMenuId(id); 134 | baseMapper.deleteById(id); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/service/impl/SysRoleMenuServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.service.impl; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysRoleMenu; 4 | import cn.tycoding.boot.modules.upms.mapper.SysRoleMenuMapper; 5 | import cn.tycoding.boot.modules.upms.service.SysRoleMenuService; 6 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 7 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | 11 | /** 12 | * 角色资源关联表(RoleMenu)表服务实现类 13 | * 14 | * @author tycoding 15 | * @since 2021/5/21 16 | */ 17 | @Service 18 | public class SysRoleMenuServiceImpl extends ServiceImpl implements SysRoleMenuService { 19 | 20 | @Override 21 | @Transactional(rollbackFor = Exception.class) 22 | public void deleteRoleMenusByRoleId(Long roleId) { 23 | baseMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, roleId)); 24 | } 25 | 26 | @Override 27 | @Transactional(rollbackFor = Exception.class) 28 | public void deleteRoleMenusByMenuId(Long menuId) { 29 | baseMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getMenuId, menuId)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/service/impl/SysRoleServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.service.impl; 2 | 3 | import cn.hutool.core.collection.CollUtil; 4 | import cn.hutool.core.lang.Dict; 5 | import cn.hutool.core.lang.tree.Tree; 6 | import cn.hutool.core.lang.tree.TreeNode; 7 | import cn.hutool.core.lang.tree.TreeUtil; 8 | import cn.tycoding.boot.common.auth.utils.AuthUtil; 9 | import cn.tycoding.boot.common.core.utils.BeanUtil; 10 | import cn.tycoding.boot.common.log.exception.ServiceException; 11 | import cn.tycoding.boot.modules.upms.dto.SysRoleDTO; 12 | import cn.tycoding.boot.modules.upms.entity.SysRole; 13 | import cn.tycoding.boot.modules.upms.entity.SysRoleMenu; 14 | import cn.tycoding.boot.modules.upms.mapper.SysRoleMapper; 15 | import cn.tycoding.boot.modules.upms.mapper.SysRoleMenuMapper; 16 | import cn.tycoding.boot.modules.upms.mapper.SysUserRoleMapper; 17 | import cn.tycoding.boot.modules.upms.service.SysRoleMenuService; 18 | import cn.tycoding.boot.modules.upms.service.SysRoleService; 19 | import cn.tycoding.boot.modules.upms.service.SysUserRoleService; 20 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 21 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 22 | import lombok.RequiredArgsConstructor; 23 | import org.springframework.stereotype.Service; 24 | import org.springframework.transaction.annotation.Transactional; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.stream.Collectors; 29 | 30 | /** 31 | * 角色表(Role)表服务实现类 32 | * 33 | * @author tycoding 34 | * @since 2021/5/21 35 | */ 36 | @Service 37 | @RequiredArgsConstructor 38 | public class SysRoleServiceImpl extends ServiceImpl implements SysRoleService { 39 | 40 | private final SysRoleMenuService sysRoleMenuService; 41 | private final SysUserRoleService sysUserRoleService; 42 | private final SysUserRoleMapper sysUserRoleMapper; 43 | private final SysRoleMenuMapper sysRoleMenuMapper; 44 | 45 | @Override 46 | public List findRolesByUserId(Long id) { 47 | return sysUserRoleMapper.getRoleListByUserId(id); 48 | } 49 | 50 | @Override 51 | public List> tree(SysRole sysRole) { 52 | List list = this.list(new LambdaQueryWrapper() 53 | .ne(SysRole::getAlias, AuthUtil.ADMINISTRATOR) 54 | .ne(sysRole.getId() != null, SysRole::getId, sysRole.getId()) 55 | ); 56 | // 构建树形结构 57 | List> nodeList = CollUtil.newArrayList(); 58 | list.forEach(t -> { 59 | TreeNode node = new TreeNode<>( 60 | t.getId(), 61 | t.getParentId(), 62 | t.getName(), 63 | 0 64 | ); 65 | node.setExtra(Dict.create().set("alias", t.getAlias()).set("des", t.getDes()).set("status", t.getStatus())); 66 | nodeList.add(node); 67 | }); 68 | return TreeUtil.build(nodeList, 0L); 69 | } 70 | 71 | private List getMenuIdsByRoleId(Long roleId) { 72 | List list = sysRoleMenuMapper.selectList(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, roleId)); 73 | return list.stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList()); 74 | } 75 | 76 | @Override 77 | public SysRoleDTO findById(Long roleId) { 78 | SysRole role = this.getById(roleId); 79 | SysRoleDTO sysRole = BeanUtil.copy(role, SysRoleDTO.class); 80 | sysRole.setMenuIds(getMenuIdsByRoleId(roleId)); 81 | return sysRole; 82 | } 83 | 84 | @Override 85 | public void add(SysRoleDTO sysRole) { 86 | sysRole.setParentId(sysRole.getParentId() == null ? 0L : sysRole.getParentId()); 87 | this.save(sysRole); 88 | addMenus(sysRole); 89 | } 90 | 91 | @Override 92 | public void update(SysRoleDTO sysRole) { 93 | sysRole.setParentId(sysRole.getParentId() == null ? 0L : sysRole.getParentId()); 94 | baseMapper.updateById(sysRole); 95 | addMenus(sysRole); 96 | } 97 | 98 | private void addMenus(SysRoleDTO sysRole) { 99 | List menuIds = sysRole.getMenuIds(); 100 | Long id = sysRole.getId(); 101 | if (menuIds != null) { 102 | // 先删除原有的关联 103 | sysRoleMenuService.deleteRoleMenusByRoleId(id); 104 | 105 | // 再新增关联 106 | List sysRoleMenuList = new ArrayList<>(); 107 | menuIds.forEach(menuId -> sysRoleMenuList.add(new SysRoleMenu() 108 | .setMenuId(menuId) 109 | .setRoleId(id))); 110 | sysRoleMenuService.saveBatch(sysRoleMenuList); 111 | } 112 | } 113 | 114 | @Override 115 | @Transactional(rollbackFor = Exception.class) 116 | public void delete(Long id) { 117 | SysRole sysRole = this.getById(id); 118 | if (AuthUtil.ADMINISTRATOR.equals(sysRole.getAlias())) { 119 | throw new ServiceException("[超级管理员]角色不可删除"); 120 | } 121 | baseMapper.deleteById(id); 122 | sysRoleMenuService.deleteRoleMenusByRoleId(id); 123 | sysUserRoleService.deleteUserRolesByRoleId(id); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/service/impl/SysUserRoleServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.service.impl; 2 | 3 | import cn.tycoding.boot.modules.upms.entity.SysRole; 4 | import cn.tycoding.boot.modules.upms.entity.SysUser; 5 | import cn.tycoding.boot.modules.upms.entity.SysUserRole; 6 | import cn.tycoding.boot.modules.upms.mapper.SysUserRoleMapper; 7 | import cn.tycoding.boot.modules.upms.service.SysUserRoleService; 8 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 9 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Transactional; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * 用户角色关联表(UserRole)表服务实现类 17 | * 18 | * @author tycoding 19 | * @since 2021/5/21 20 | */ 21 | @Service 22 | public class SysUserRoleServiceImpl extends ServiceImpl implements SysUserRoleService { 23 | 24 | @Override 25 | public List getUserListByRoleId(Long roleId) { 26 | return baseMapper.getUserListByRoleId(roleId); 27 | } 28 | 29 | @Override 30 | public List getRoleListByUserId(Long userId) { 31 | return baseMapper.getRoleListByUserId(userId); 32 | } 33 | 34 | @Override 35 | @Transactional(rollbackFor = Exception.class) 36 | public void deleteUserRolesByUserId(Long userId) { 37 | baseMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId)); 38 | } 39 | 40 | @Override 41 | @Transactional(rollbackFor = Exception.class) 42 | public void deleteUserRolesByRoleId(Long roleId) { 43 | baseMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getRoleId, roleId)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/cn/tycoding/boot/modules/upms/service/impl/SysUserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot.modules.upms.service.impl; 2 | 3 | import cn.tycoding.boot.common.auth.utils.AuthUtil; 4 | import cn.tycoding.boot.common.core.api.QueryPage; 5 | import cn.tycoding.boot.common.core.constant.CacheConstant; 6 | import cn.tycoding.boot.common.core.constant.CommonConstant; 7 | import cn.tycoding.boot.common.core.utils.BeanUtil; 8 | import cn.tycoding.boot.common.core.utils.Is; 9 | import cn.tycoding.boot.common.log.exception.ServiceException; 10 | import cn.tycoding.boot.common.mybatis.utils.MybatisUtil; 11 | import cn.tycoding.boot.modules.auth.dto.UserInfo; 12 | import cn.tycoding.boot.modules.auth.exception.TumoAuth2Exception; 13 | import cn.tycoding.boot.modules.upms.dto.SysUserDTO; 14 | import cn.tycoding.boot.modules.upms.entity.*; 15 | import cn.tycoding.boot.modules.upms.mapper.SysUserMapper; 16 | import cn.tycoding.boot.modules.upms.service.*; 17 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 18 | import com.baomidou.mybatisplus.core.metadata.IPage; 19 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 20 | import lombok.RequiredArgsConstructor; 21 | import org.apache.commons.lang3.StringUtils; 22 | import org.springframework.cache.annotation.CacheEvict; 23 | import org.springframework.cache.annotation.Cacheable; 24 | import org.springframework.stereotype.Service; 25 | import org.springframework.transaction.annotation.Transactional; 26 | 27 | import java.util.ArrayList; 28 | import java.util.Date; 29 | import java.util.List; 30 | import java.util.Set; 31 | import java.util.stream.Collectors; 32 | 33 | /** 34 | * 用户表(User)表服务实现类 35 | * 36 | * @author tycoding 37 | * @since 2021/5/21 38 | */ 39 | @Service 40 | @RequiredArgsConstructor 41 | public class SysUserServiceImpl extends ServiceImpl implements SysUserService { 42 | 43 | private final SysRoleService sysRoleService; 44 | private final SysMenuService sysMenuService; 45 | private final SysDeptService sysDeptService; 46 | private final SysUserRoleService sysUserRoleService; 47 | 48 | @Override 49 | public SysUser findByName(String username) { 50 | return baseMapper.selectOne(new LambdaQueryWrapper().eq(SysUser::getUsername, username)); 51 | } 52 | 53 | @Override 54 | public SysUserDTO findById(Long userId) { 55 | SysUser sysUser = baseMapper.selectById(userId); 56 | SysUserDTO dto = BeanUtil.copy(sysUser, SysUserDTO.class); 57 | dto.setPassword(null); 58 | List roleIds = 59 | sysUserRoleService.getRoleListByUserId(userId).stream().map(SysRole::getId).collect(Collectors.toList()); 60 | dto.setRoleIds(roleIds); 61 | return dto; 62 | } 63 | 64 | @Override 65 | @Cacheable(value = CacheConstant.USER_DETAIL_KEY, key = "#username") 66 | public UserInfo info(String username) { 67 | return this.build(new UserInfo().setUser(this.findByName(username))); 68 | } 69 | 70 | /** 71 | * 构建用户信息、角色信息、权限标识信息、部门信息 72 | */ 73 | private UserInfo build(UserInfo userInfo) { 74 | if (userInfo == null || userInfo.getUser() == null) { 75 | throw new ServiceException("没有查询用用户信息"); 76 | } 77 | //获取用户角色列表 78 | List sysRoleList = sysRoleService.findRolesByUserId(userInfo.getUser().getId()); 79 | if (sysRoleList.size() == 0) { 80 | throw new TumoAuth2Exception(AuthUtil.NOT_ROLE_ERROR); 81 | } 82 | 83 | //获取用户权限列表 84 | List menuList = new ArrayList<>(); 85 | long isAdmin = sysRoleList.stream().filter(role -> AuthUtil.ADMINISTRATOR.equals(role.getAlias())).count(); 86 | if (isAdmin > 0) { 87 | // 包含了超级管理员角色,拥有所有权限 88 | menuList = sysMenuService.list(); 89 | } else { 90 | // 根据角色筛选权限 91 | menuList = sysMenuService.getUserMenuList(sysRoleList); 92 | } 93 | Set perms = 94 | menuList.stream().map(SysMenu::getPerms).filter(StringUtils::isNotEmpty).collect(Collectors.toSet()); 95 | 96 | //获取用户部门信息 97 | SysDept sysDept = sysDeptService.getById(userInfo.getUser().getDeptId()); 98 | 99 | return userInfo.setRoles(sysRoleList).setPerms(perms).setDept(sysDept); 100 | } 101 | 102 | @Override 103 | public List list(SysUser sysUser) { 104 | List list = baseMapper.selectList(new LambdaQueryWrapper().ne(SysUser::getUsername, 105 | AuthUtil.ADMINISTRATOR).like(StringUtils.isNotEmpty(sysUser.getUsername()), SysUser::getUsername, 106 | sysUser.getUsername())); 107 | list.forEach(i -> i.setPassword(null)); 108 | return list; 109 | } 110 | 111 | @Override 112 | public IPage page(SysUserDTO user, QueryPage queryPage) { 113 | return baseMapper.page(MybatisUtil.wrap(user, queryPage), user, AuthUtil.getUserId(), AuthUtil.ADMINISTRATOR); 114 | } 115 | 116 | @Override 117 | public boolean checkName(SysUserDTO sysUser) { 118 | if (AuthUtil.ADMINISTRATOR.equals(sysUser.getUsername())) { 119 | return false; 120 | } 121 | LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper().eq(SysUser::getUsername, 122 | sysUser.getUsername()); 123 | if (sysUser.getId() != null && sysUser.getId() != 0) { 124 | queryWrapper.ne(SysUser::getId, sysUser.getId()); 125 | } 126 | return baseMapper.selectList(queryWrapper).size() <= 0; 127 | } 128 | 129 | @Override 130 | @Transactional(rollbackFor = Exception.class) 131 | public void add(SysUserDTO user) { 132 | if (!checkName(user)) { 133 | throw new ServiceException("该用户名已存在,请重新输入!"); 134 | } 135 | 136 | user.setCreateTime(new Date()); 137 | user.setPassword(AuthUtil.encode(user.getPassword())); 138 | 139 | // 设置默认头像 140 | if (StringUtils.isEmpty(user.getAvatar())) { 141 | user.setAvatar(CommonConstant.DEFAULT_AVATAR); 142 | } 143 | 144 | // 设置角色 145 | if (Is.isEmpty(user.getRoleIds())) { 146 | throw new ServiceException("用户角色不能为空"); 147 | } 148 | baseMapper.insert(user); 149 | addRole(user); 150 | } 151 | 152 | private void addRole(SysUserDTO user) { 153 | List roleIds = user.getRoleIds(); 154 | Long userId = user.getId(); 155 | if (roleIds != null) { 156 | // 删除之前用户与角色表之前的关联,并重新建立关联 157 | sysUserRoleService.deleteUserRolesByUserId(userId); 158 | 159 | // 新增用户角色关联 160 | List list = new ArrayList<>(); 161 | roleIds.forEach(roleId -> list.add(new SysUserRole().setUserId(userId).setRoleId(roleId))); 162 | sysUserRoleService.saveBatch(list); 163 | } 164 | } 165 | 166 | @Override 167 | @Transactional(rollbackFor = Exception.class) 168 | @CacheEvict(value = CacheConstant.USER_DETAIL_KEY, key = "#user.username") 169 | public void update(SysUserDTO user) { 170 | if (!checkName(user)) { 171 | throw new ServiceException("该用户名已存在,请重新输入!"); 172 | } 173 | 174 | // 设置默认头像 175 | if (StringUtils.isEmpty(user.getAvatar())) { 176 | user.setAvatar(CommonConstant.DEFAULT_AVATAR); 177 | } 178 | user.setPassword(null); 179 | baseMapper.updateById(user); 180 | addRole(user); 181 | } 182 | 183 | @Override 184 | @Transactional(rollbackFor = Exception.class) 185 | @CacheEvict(value = CacheConstant.USER_DETAIL_KEY, key = "#username") 186 | public void delete(Long id, String username) { 187 | baseMapper.deleteById(id); 188 | sysUserRoleService.deleteUserRolesByUserId(id); 189 | } 190 | 191 | @Override 192 | @Transactional(rollbackFor = Exception.class) 193 | @CacheEvict(value = CacheConstant.USER_DETAIL_KEY, key = "#username") 194 | public void reset(Long id, String password, String username) { 195 | SysUser user = new SysUser(); 196 | user.setId(id); 197 | user.setPassword(AuthUtil.encode(password)); 198 | baseMapper.updateById(user); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | cn.tycoding.boot.common.log.LogAutoConfiguration,\ 3 | cn.tycoding.boot.common.log.exception.RestExceptionTranslator,\ 4 | cn.tycoding.boot.common.log.exception.GlobalExceptionTranslator 5 | -------------------------------------------------------------------------------- /src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | # 数据库配置 3 | datasource: 4 | username: root 5 | password: root 6 | url: jdbc:mysql://tumo-boot-mysql:3306/tumo_boot?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true 7 | 8 | # Redis配置 9 | redis: 10 | port: 6379 11 | host: tumo-boot-redis 12 | 13 | # 自定义配置 14 | tumo-boot: 15 | # Oss配置 16 | file: 17 | remote-path: http://127.0.0.1:80/upload 18 | # 生产环境下,文件上传的地址应该是Web容器映射的可访问的地址,这里设置为nginx默认映射地址 19 | upload-path: /usr/local/var/www/upload 20 | -------------------------------------------------------------------------------- /src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | # 数据库配置 3 | datasource: 4 | username: root 5 | password: root 6 | url: jdbc:mysql://tumo-boot-mysql:3306/tumo_boot?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true 7 | 8 | # Redis配置 9 | redis: 10 | port: 6379 11 | host: tumo-boot-redis 12 | 13 | # 自定义配置 14 | tumo-boot: 15 | # Oss配置 16 | file: 17 | remote-path: http://127.0.0.1:80/ 18 | # 生产环境下,文件上传的地址应该是Web容器映射的可访问的地址 19 | upload-path: /usr/share/nginx/html 20 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8090 3 | tomcat: 4 | uri-encoding: utf-8 5 | 6 | spring: 7 | # 应用名称 8 | application: 9 | name: tumo-boot 10 | # 默认执行的配置文件 11 | profiles: 12 | active: dev 13 | main: 14 | allow-bean-definition-overriding: true 15 | 16 | # 数据库配置 17 | datasource: 18 | type: com.alibaba.druid.pool.DruidDataSource 19 | driver-class-name: com.mysql.cj.jdbc.Driver 20 | 21 | # Cache设置 22 | cache: 23 | type: redis 24 | 25 | # 文件上传相关设置 26 | servlet: 27 | multipart: 28 | max-file-size: 200MB 29 | max-request-size: 200MB 30 | 31 | # MybatisPlus配置 32 | mybatis-plus: 33 | type-aliases-package: cn.tycoding.boot.modules.**.entity 34 | mapper-locations: classpath:mapper/**/*.xml 35 | configuration: 36 | jdbc-type-for-null: null 37 | global-config: 38 | banner: false 39 | 40 | # 系统自定义配置 41 | tumo-boot: 42 | # 权限配置 43 | auth: 44 | # 是否开启演示环境 45 | is-demo-env: true 46 | # 默认忽略拦截的URL 47 | skip-url: 48 | - /favicon.ico 49 | - /upload/** 50 | - /tumo-boot/oss/put 51 | - /tumo-boot/auth/logout 52 | - /tumo-boot/auth/captcha 53 | # 日志配置 54 | log: 55 | # 是否开始日志打印 56 | enable: false 57 | # Mybatis 配置 58 | mybatis: 59 | # 是否开启SQL打印 60 | enable: false 61 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ${AnsiColor.BLUE} ___ ___ ___ ___ ___ ___ ___ ___ 2 | ${AnsiColor.BLUE} /\ \ /\__\ /\__\ /\ \ /\ \ /\ \ /\ \ /\ \ 3 | ${AnsiColor.BLUE} \:\ \ /:/ _/_ /::L_L_ /::\ \ /::\ \ /::\ \ /::\ \ \:\ \ 4 | ${AnsiColor.BLUE} /::\__\ /:/_/\__\ /:/L:\__\ /:/\:\__\ /::\:\__\ /:/\:\__\ /:/\:\__\ /::\__\ 5 | ${AnsiColor.BLUE} /:/\/__/ \:\/:/ / \/_/:/ / \:\/:/ / \:\::/ / \:\/:/ / \:\/:/ / /:/\/__/ 6 | ${AnsiColor.BLUE} \/__/ \::/ / /:/ / \::/ / \::/ / \::/ / \::/ / \/__/ 7 | ${AnsiColor.BLUE} \/__/ \/__/ \/__/ \/__/ \/__/ \/__/ 8 | 9 | ${AnsiColor.BLUE} ${spring.application.name} :: ${AnsiColor.CYAN} Running SpringBoot ${spring-boot.version} ${AnsiColor.RED} :: Port: ${server.port} :: ${AnsiColor.WHITE} 10 | 11 | 开发文档:http://docs.boot.tycoding.cn/ 12 | 开源地址:https://github.com/Tumo-Team/tumo-boot 13 | 博客: http://tycoding.cn 14 | Github: https://github.com/tycoding 15 | QQ交流群: 866685601 16 | 17 | -------------------------------------------------------------------------------- /src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 12 | 14 | 15 | 16 | 17 | ${CONSOLE_LOG_PATTERN} 18 | 19 | 20 | 21 | 22 | 23 | ${log.path}/debug.log 24 | 25 | ${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz 26 | 50MB 27 | 30 28 | 29 | 30 | %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n 31 | 32 | 33 | 34 | 35 | 36 | ${log.path}/error.log 37 | 38 | ${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz 39 | 50MB 40 | 30 41 | 42 | 43 | %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n 44 | 45 | 46 | ERROR 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/main/resources/mapper/upms/SysMenuMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/resources/mapper/upms/SysUserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 49 | 50 | -------------------------------------------------------------------------------- /src/main/resources/mapper/upms/SysUserRoleMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 15 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/resources/static/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tumo-Team/tumo-boot/f110e7741ae70c2a9c21e368965aa0acfacf39cd/src/main/resources/static/default.png -------------------------------------------------------------------------------- /src/test/java/cn/tycoding/boot/TumoTest.java: -------------------------------------------------------------------------------- 1 | package cn.tycoding.boot; 2 | 3 | import org.junit.Test; 4 | 5 | /** 6 | * 测试类,如果开启了@RunWith和@SpringBootTest注解,将启动Spring容器,否则就是正常的测试类 7 | * 8 | * @author tycoding 9 | * @since 2021/1/29 10 | */ 11 | //@RunWith(SpringRunner.class) 12 | //@SpringBootTest(classes = TumoBootApp.class) 13 | public class TumoTest { 14 | 15 | @Test 16 | public void test() { 17 | System.out.println("Hello Tumo-Team~"); 18 | } 19 | 20 | } 21 | --------------------------------------------------------------------------------