From 54acf90f4e8e9b992d70c50ca2d710b7e6e796a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=AE=B6=E8=AE=AD?= Date: Wed, 19 Aug 2026 14:45:24 +0800 Subject: [PATCH] =?UTF-8?q?Initial=20commit:=20Freedom=20Admin=20=E6=9D=83?= =?UTF-8?q?=E9=99=90=E7=AE=A1=E7=90=86=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 36 ++ .mvn/wrapper/MavenWrapperDownloader.java | 118 ++++ .mvn/wrapper/maven-wrapper.properties | 2 + Freedom_Admin_代码审查报告.md | 479 ++++++++++++++++ HELP.md | 26 + README.en.md | 36 ++ README.md | 226 ++++++++ SQLER图.md | 379 ++++++++++++ mvnw | 310 ++++++++++ mvnw.cmd | 182 ++++++ pom.xml | 180 ++++++ sql/init.sql | 0 .../com/freedom/admin/AdminApplication.java | 20 + .../freedom/admin/base/BaseController.java | 53 ++ .../java/com/freedom/admin/common/Result.java | 99 ++++ .../com/freedom/admin/common/ResultCode.java | 26 + .../admin/config/GlobalExceptionHandler.java | 39 ++ .../config/JwtAuthenticationTokenFilter.java | 61 ++ .../MyAuthenticationFailureHandler.java | 35 ++ .../MyAuthenticationSuccessHandler.java | 44 ++ .../com/freedom/admin/config/MyException.java | 27 + .../config/RestAuthenticationEntryPoint.java | 29 + .../admin/config/WebSecurityConfig.java | 124 ++++ .../admin/controller/GeneratorController.java | 541 ++++++++++++++++++ .../admin/controller/TSysDeptController.java | 111 ++++ .../controller/TSysPermissionController.java | 157 +++++ .../admin/controller/TSysRoleController.java | 105 ++++ .../admin/controller/TSysUserController.java | 130 +++++ .../freedom/admin/entity/GeneratorConfig.java | 103 ++++ .../admin/entity/GeneratorTableVO.java | 58 ++ .../com/freedom/admin/entity/TSysDept.java | 76 +++ .../freedom/admin/entity/TSysPermission.java | 89 +++ .../com/freedom/admin/entity/TSysRole.java | 74 +++ .../admin/entity/TSysRolePermission.java | 39 ++ .../com/freedom/admin/entity/TSysUser.java | 94 +++ .../freedom/admin/entity/TSysUserRole.java | 39 ++ .../freedom/admin/mapper/GeneratorMapper.java | 29 + .../freedom/admin/mapper/TSysDeptMapper.java | 32 ++ .../admin/mapper/TSysPermissionMapper.java | 61 ++ .../freedom/admin/mapper/TSysRoleMapper.java | 23 + .../mapper/TSysRolePermissionMapper.java | 13 + .../freedom/admin/mapper/TSysUserMapper.java | 16 + .../admin/mapper/TSysUserRoleMapper.java | 13 + .../admin/service/GeneratorService.java | 30 + .../admin/service/TSysDeptService.java | 29 + .../admin/service/TSysPermissionService.java | 52 ++ .../service/TSysRolePermissionService.java | 15 + .../admin/service/TSysRoleService.java | 21 + .../admin/service/TSysUserService.java | 23 + .../freedom/admin/service/UserService.java | 12 + .../service/impl/GeneratorServiceImpl.java | 40 ++ .../admin/service/impl/TSysDeptImpl.java | 120 ++++ .../impl/TSysPermissionServiceImpl.java | 231 ++++++++ .../impl/TSysRolePermissionServiceImpl.java | 55 ++ .../service/impl/TSysRoleServiceImpl.java | 28 + .../service/impl/TSysUserServiceImpl.java | 130 +++++ .../admin/service/impl/UserServiceImpl.java | 56 ++ .../com/freedom/admin/utils/AlipayBean.java | 39 ++ .../com/freedom/admin/utils/JwtUtils.java | 75 +++ .../freedom/admin/utils/SimilarityUtil.java | 73 +++ src/main/resources/application.yml | 73 +++ src/main/resources/btl/apiJs.btl | 50 ++ src/main/resources/btl/controller.btl | 98 ++++ src/main/resources/btl/entity.btl | 27 + src/main/resources/btl/mapper.btl | 13 + src/main/resources/btl/mapperXml.btl | 30 + src/main/resources/btl/service.btl | 13 + src/main/resources/btl/serviceImpl.btl | 18 + src/main/resources/btl/view.btl | 350 +++++++++++ src/main/resources/mapper/GeneratorMapper.xml | 37 ++ src/main/resources/mapper/TSysDeptMapper.xml | 50 ++ .../resources/mapper/TSysPermissionMapper.xml | 143 +++++ src/main/resources/mapper/TSysRoleMapper.xml | 31 + .../mapper/TSysRolePermissionMapper.xml | 20 + src/main/resources/mapper/TSysUserMapper.xml | 32 ++ .../resources/mapper/TSysUserRoleMapper.xml | 20 + .../freedom/admin/AdminApplicationTests.java | 13 + 代码评审文档.md | 264 +++++++++ 新建文本文档.txt | 0 系统架构流程图.md | 226 ++++++++ 80 files changed, 6871 insertions(+) create mode 100644 .gitignore create mode 100644 .mvn/wrapper/MavenWrapperDownloader.java create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100644 Freedom_Admin_代码审查报告.md create mode 100644 HELP.md create mode 100644 README.en.md create mode 100644 README.md create mode 100644 SQLER图.md create mode 100644 mvnw create mode 100644 mvnw.cmd create mode 100644 pom.xml create mode 100644 sql/init.sql create mode 100644 src/main/java/com/freedom/admin/AdminApplication.java create mode 100644 src/main/java/com/freedom/admin/base/BaseController.java create mode 100644 src/main/java/com/freedom/admin/common/Result.java create mode 100644 src/main/java/com/freedom/admin/common/ResultCode.java create mode 100644 src/main/java/com/freedom/admin/config/GlobalExceptionHandler.java create mode 100644 src/main/java/com/freedom/admin/config/JwtAuthenticationTokenFilter.java create mode 100644 src/main/java/com/freedom/admin/config/MyAuthenticationFailureHandler.java create mode 100644 src/main/java/com/freedom/admin/config/MyAuthenticationSuccessHandler.java create mode 100644 src/main/java/com/freedom/admin/config/MyException.java create mode 100644 src/main/java/com/freedom/admin/config/RestAuthenticationEntryPoint.java create mode 100644 src/main/java/com/freedom/admin/config/WebSecurityConfig.java create mode 100644 src/main/java/com/freedom/admin/controller/GeneratorController.java create mode 100644 src/main/java/com/freedom/admin/controller/TSysDeptController.java create mode 100644 src/main/java/com/freedom/admin/controller/TSysPermissionController.java create mode 100644 src/main/java/com/freedom/admin/controller/TSysRoleController.java create mode 100644 src/main/java/com/freedom/admin/controller/TSysUserController.java create mode 100644 src/main/java/com/freedom/admin/entity/GeneratorConfig.java create mode 100644 src/main/java/com/freedom/admin/entity/GeneratorTableVO.java create mode 100644 src/main/java/com/freedom/admin/entity/TSysDept.java create mode 100644 src/main/java/com/freedom/admin/entity/TSysPermission.java create mode 100644 src/main/java/com/freedom/admin/entity/TSysRole.java create mode 100644 src/main/java/com/freedom/admin/entity/TSysRolePermission.java create mode 100644 src/main/java/com/freedom/admin/entity/TSysUser.java create mode 100644 src/main/java/com/freedom/admin/entity/TSysUserRole.java create mode 100644 src/main/java/com/freedom/admin/mapper/GeneratorMapper.java create mode 100644 src/main/java/com/freedom/admin/mapper/TSysDeptMapper.java create mode 100644 src/main/java/com/freedom/admin/mapper/TSysPermissionMapper.java create mode 100644 src/main/java/com/freedom/admin/mapper/TSysRoleMapper.java create mode 100644 src/main/java/com/freedom/admin/mapper/TSysRolePermissionMapper.java create mode 100644 src/main/java/com/freedom/admin/mapper/TSysUserMapper.java create mode 100644 src/main/java/com/freedom/admin/mapper/TSysUserRoleMapper.java create mode 100644 src/main/java/com/freedom/admin/service/GeneratorService.java create mode 100644 src/main/java/com/freedom/admin/service/TSysDeptService.java create mode 100644 src/main/java/com/freedom/admin/service/TSysPermissionService.java create mode 100644 src/main/java/com/freedom/admin/service/TSysRolePermissionService.java create mode 100644 src/main/java/com/freedom/admin/service/TSysRoleService.java create mode 100644 src/main/java/com/freedom/admin/service/TSysUserService.java create mode 100644 src/main/java/com/freedom/admin/service/UserService.java create mode 100644 src/main/java/com/freedom/admin/service/impl/GeneratorServiceImpl.java create mode 100644 src/main/java/com/freedom/admin/service/impl/TSysDeptImpl.java create mode 100644 src/main/java/com/freedom/admin/service/impl/TSysPermissionServiceImpl.java create mode 100644 src/main/java/com/freedom/admin/service/impl/TSysRolePermissionServiceImpl.java create mode 100644 src/main/java/com/freedom/admin/service/impl/TSysRoleServiceImpl.java create mode 100644 src/main/java/com/freedom/admin/service/impl/TSysUserServiceImpl.java create mode 100644 src/main/java/com/freedom/admin/service/impl/UserServiceImpl.java create mode 100644 src/main/java/com/freedom/admin/utils/AlipayBean.java create mode 100644 src/main/java/com/freedom/admin/utils/JwtUtils.java create mode 100644 src/main/java/com/freedom/admin/utils/SimilarityUtil.java create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/btl/apiJs.btl create mode 100644 src/main/resources/btl/controller.btl create mode 100644 src/main/resources/btl/entity.btl create mode 100644 src/main/resources/btl/mapper.btl create mode 100644 src/main/resources/btl/mapperXml.btl create mode 100644 src/main/resources/btl/service.btl create mode 100644 src/main/resources/btl/serviceImpl.btl create mode 100644 src/main/resources/btl/view.btl create mode 100644 src/main/resources/mapper/GeneratorMapper.xml create mode 100644 src/main/resources/mapper/TSysDeptMapper.xml create mode 100644 src/main/resources/mapper/TSysPermissionMapper.xml create mode 100644 src/main/resources/mapper/TSysRoleMapper.xml create mode 100644 src/main/resources/mapper/TSysRolePermissionMapper.xml create mode 100644 src/main/resources/mapper/TSysUserMapper.xml create mode 100644 src/main/resources/mapper/TSysUserRoleMapper.xml create mode 100644 src/test/java/com/freedom/admin/AdminApplicationTests.java create mode 100644 代码评审文档.md create mode 100644 新建文本文档.txt create mode 100644 系统架构流程图.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7d02bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +/out/ + +### Maven ### +target/ + +### WorkBuddy ### +.workbuddy/ diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..a45eb6b --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..642d572 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/Freedom_Admin_代码审查报告.md b/Freedom_Admin_代码审查报告.md new file mode 100644 index 0000000..798311b --- /dev/null +++ b/Freedom_Admin_代码审查报告.md @@ -0,0 +1,479 @@ +# Freedom Admin 管理系统代码全面审查报告 + +## 📊 审查概况 + +| 项目 | 详情 | +|------|------| +| **审查文件数量** | 49个Java文件 + 7个XML文件 + 1个YAML配置 | +| **主要技术栈** | Spring Boot 2.7.10 + Spring Security + MyBatis-Plus + JWT | +| **审查时间** | 2024年11月3日 | +| **审查重点** | 功能正确性、性能、安全、可维护性、编码规范 | + +## 🚨 紧急问题(P0 - 必须立即修复) + +### 1. 严重安全漏洞 - 硬编码敏感信息 + +**文件位置**: `src/main/resources/application.yml:13-14, 35` + +**问题代码**: +```yaml +# 数据库密码硬编码 +password: 1Qaz@4772 + +# 邮件授权码硬编码 +password: nsbrkfdzzmdlbddi +``` + +**风险等级**: 🔴 **高危** - 可能导致完整系统被入侵,数据泄露 + +**影响范围**: +- 数据库服务器安全 +- 邮件账户安全 +- 系统整体安全性 + +**修复建议**: +```java +// 使用环境变量管理敏感信息 +spring: + datasource: + password: ${DB_PASSWORD} + mail: + password: ${MAIL_PASSWORD} +``` + +### 2. JWT安全配置缺陷 + +**文件位置**: `src/main/java/com/freedom/admin/utils/JwtUtils.java:33` + +**问题代码**: +```java +.signWith(SignatureAlgorithm.HS512,secret) // 使用HS512但密钥强度不足 +``` + +**风险等级**: 🔴 **高危** - JWT密钥可能被暴力破解 + +**安全影响**: +- 令牌伪造风险 +- 身份认证绕过 +- 权限提升攻击 + +**修复建议**: +```java +// 生成更强的密钥 +@Value("${jwt.secret}") +private String secret; // 确保密钥长度至少256位 + +// 或使用RSA算法 +.signWith(SignatureAlgorithm.RS256, privateKey) +``` + +## ⚠️ 高优先级问题(P1 - 建议本周修复) + +### 3. 用户注册功能安全隐患 + +**文件位置**: `src/main/java/com/freedom/admin/service/impl/TSysUserServiceImpl.java:47` + +**问题代码**: +```java +String encode = bCryptPasswordEncoder.encode("123456"); // 硬编码默认密码 +``` + +**问题描述**: 所有新建用户使用相同的默认密码"123456" + +**安全风险**: +- 弱密码策略 +- 批量账户安全风险 +- 密码猜测攻击 + +**修复建议**: +```java +// 方案1: 生成随机密码 +String randomPassword = generateRandomPassword(12); + +// 方案2: 强制首次登录修改密码 +user.setForcePasswordChange(true); + +// 方案3: 发送密码重置链接 +sendPasswordResetEmail(user.getEmail()); +``` + +### 4. 输入验证全面缺失 + +**文件位置**: 所有Controller和实体类 + +**问题示例**: `src/main/java/com/freedom/admin/controller/TSysUserController.java:45` +```java +public Result register(TSysUser sysUser){ // 无输入验证 +``` + +**风险**: +- SQL注入风险 +- XSS攻击风险 +- 数据完整性破坏 + +**修复方案**: +```java +// 创建DTO类并添加验证注解 +@Data +public class UserRegisterDTO { + @NotBlank(message = "用户名不能为空") + @Size(min = 3, max = 50, message = "用户名长度必须在3-50之间") + private String username; + + @Email(message = "邮箱格式不正确") + private String email; + + @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$", + message = "密码必须包含大小写字母和数字,长度至少8位") + private String password; +} + +// Controller中使用验证 +@PostMapping("/register") +public Result register(@Valid @RequestBody UserRegisterDTO dto) { + // 业务逻辑 +} +``` + +### 5. 异常处理过于笼统 + +**文件位置**: `src/main/java/com/freedom/admin/config/GlobalExceptionHandler.java:21-26` + +**问题代码**: +```java +@ExceptionHandler(Exception.class) // 捕获所有异常 +public Result error(Exception e){ + e.printStackTrace(); // 生产环境不应打印堆栈 + return Result.error().message("执行了全局异常"); // 模糊错误信息 +} +``` + +**问题**: +- 敏感信息泄露风险 +- 调试信息暴露系统结构 +- 用户体验差 + +**改进方案**: +```java +@ExceptionHandler(BusinessException.class) +public Result handleBusinessException(BusinessException e) { + log.warn("业务异常: {}", e.getMessage()); + return Result.error().code(e.getCode()).message(e.getMessage()); +} + +@ExceptionHandler(ValidationException.class) +public Result handleValidationException(ValidationException e) { + log.warn("参数验证失败: {}", e.getMessage()); + return Result.error().code(400).message("参数验证失败"); +} + +@ExceptionHandler(Exception.class) +public Result handleSystemException(Exception e) { + log.error("系统异常", e); + return Result.error().message("系统繁忙,请稍后重试"); +} +``` + +## 📋 中优先级问题(P2 - 建议本月修复) + +### 6. SQL查询性能问题 + +**文件位置**: `src/main/java/com/freedom/admin/controller/TSysUserController.java:79-84` + +**问题代码**: +```java +Page page = tSysUserService.page(new Page<>(pageNum,pageSize), queryWrapper); +``` + +**性能影响**: +- 未使用MyBatis-Plus分页插件优化 +- 可能产生全表扫描 +- 大数据量时分页性能差 + +**优化方案**: +```java +// 配置分页拦截器 +@Configuration +public class MybatisPlusConfig { + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return interceptor; + } +} + +// 使用优化后的分页 +Page page = tSysUserService.page( + new Page<>(pageNum, pageSize), + queryWrapper.orderByDesc("create_date") +); +``` + +### 7. 密码加密强度不足 + +**文件位置**: `src/main/java/com/freedom/admin/config/WebSecurityConfig.java:94` + +**问题代码**: +```java +return new BCryptPasswordEncoder(); // 未指定强度 +``` + +**安全风险**: 加密强度不足可能被暴力破解 + +**改进方案**: +```java +@Bean +public PasswordEncoder passwordEncoder() { + // 使用更高的加密强度(10-12) + return new BCryptPasswordEncoder(12); +} +``` + +### 8. 跨域配置硬编码 + +**文件位置**: `src/main/java/com/freedom/admin/config/WebSecurityConfig.java:112` + +**问题代码**: +```java +cfg.addAllowedOrigin("http://localhost:8080"); +``` + +**问题**: 多环境部署时需修改代码 + +**改进方案**: +```java +@Value("${cors.allowed-origins}") +private String[] allowedOrigins; + +@Bean +public CorsConfigurationSource corsConfigurationSource() { + return request -> { + CorsConfiguration cfg = new CorsConfiguration(); + cfg.setAllowedOrigins(Arrays.asList(allowedOrigins)); + cfg.addAllowedHeader("*"); + cfg.addAllowedMethod("*"); + cfg.setAllowCredentials(true); + return cfg; + }; +} +``` + +## 📝 低优先级问题(P3 - 可后续优化) + +### 9. 代码规范问题 + +#### 9.1 空注释和不规范注释 +**文件**: 多个文件存在空描述 +```java +/** + * @description // 空描述 + */ +``` + +#### 9.2 方法命名不规范 +**文件**: `src/main/java/com/freedom/admin/utils/JwtUtils.java:70` +```java +public String getUsernameFormToken(String token){ // 应为 getUsernameFromToken +``` + +#### 9.3 日志记录不统一 +**问题**: 部分使用`@Log4j2`,部分使用`@Slf4j` + +### 10. 配置优化 + +#### 10.1 连接池配置保守 +**文件**: `src/main/resources/application.yml:18` +```yaml +maximum-pool-size: 10 # 可根据实际负载调整 +``` + +#### 10.2 缺少缓存配置 +**建议**: 添加Redis缓存支持 + +#### 10.3 邮件服务配置优化 +**建议**: 添加重试机制和异步发送 + +## 🔧 具体修复方案 + +### 紧急修复(P0)实施步骤 + +#### 步骤1: 移除硬编码密码 +```bash +# 1. 设置环境变量 +export DB_PASSWORD=your_secure_password +export MAIL_PASSWORD=your_mail_password + +# 2. 修改application.yml +spring: + datasource: + password: ${DB_PASSWORD} + mail: + password: ${MAIL_PASSWORD} +``` + +#### 步骤2: 加强JWT安全 +```java +// 生成256位随机密钥 +@Value("${jwt.secret}") +private String secret; // 确保配置文件中的密钥长度足够 + +// 或使用RSA非对称加密 +@Bean +public KeyPair keyPair() { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + return keyPairGenerator.generateKeyPair(); +} +``` + +#### 步骤3: 禁用生产环境堆栈打印 +```java +@Profile("!prod") +public void printStackTrace() { + // 仅开发环境打印堆栈 +} +``` + +### 高优先级修复(P1)实施步骤 + +#### 输入验证实施 +```java +// 1. 创建验证DTO +@Data +public class UserRegisterDTO { + @NotBlank(message = "用户名不能为空") + @Size(min = 3, max = 50) + private String username; + + @Email + private String email; + + @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$") + private String password; +} + +// 2. Controller使用验证 +@PostMapping("/register") +public Result register(@Valid @RequestBody UserRegisterDTO dto, + BindingResult result) { + if (result.hasErrors()) { + return Result.error().message(result.getFieldError().getDefaultMessage()); + } + // 业务逻辑 +} +``` + +## 📊 总体评估 + +### 安全风险评估矩阵 + +| 风险类型 | 风险等级 | 影响程度 | 发生概率 | 综合评分 | +|----------|----------|----------|----------|----------| +| 敏感信息泄露 | 高危 | 严重 | 高 | 9.5/10 | +| JWT安全缺陷 | 高危 | 严重 | 中 | 8.5/10 | +| 输入验证缺失 | 中危 | 中等 | 高 | 7.0/10 | +| 异常信息泄露 | 中危 | 中等 | 中 | 6.5/10 | +| 性能问题 | 低危 | 低等 | 中 | 4.0/10 | + +### 维度评分表 + +| 评估维度 | 评分 | 说明 | 改进建议 | +|----------|------|------|----------| +| **安全性** | 4/10 | 存在严重安全漏洞 | 优先修复P0、P1问题 | +| **功能性** | 8/10 | 核心功能完整,逻辑正确 | 保持现有功能稳定性 | +| **性能** | 6/10 | 基础优化,有提升空间 | 添加缓存和查询优化 | +| **可维护性** | 7/10 | 结构清晰,但文档不足 | 完善注释和文档 | +| **代码规范** | 6/10 | 基本规范,需细节优化 | 统一代码风格 | + +**总体评分**: 6.2/10 + +## 🎯 改进路线图 + +### 短期目标(1-2周) +- [ ] **P0**: 修复硬编码敏感信息漏洞 +- [ ] **P0**: 加强JWT安全配置 +- [ ] **P1**: 添加完整的输入验证机制 +- [ ] **P1**: 改进异常处理策略 +- [ ] **P1**: 修复用户注册密码安全问题 + +### 中期目标(1个月) +- [ ] **P2**: 优化SQL查询性能 +- [ ] **P2**: 提高密码加密强度 +- [ ] **P2**: 配置化跨域设置 +- [ ] **P3**: 统一代码规范和日志 +- [ ] **P3**: 添加基础单元测试 + +### 长期目标(3个月) +- [ ] 引入Redis缓存机制 +- [ ] 实现微服务架构改造 +- [ ] 建立完整的监控体系 +- [ ] 实现自动化部署流程 +- [ ] 完善API文档和开发文档 + +## 📋 技术债务清单 + +### 高优先级技术债务 +1. **安全加固** - 敏感信息管理、加密强度提升 +2. **输入验证** - 全面的数据验证机制 +3. **异常处理** - 精细化异常分类和处理 + +### 中优先级技术债务 +1. **性能优化** - 数据库查询优化、缓存引入 +2. **配置管理** - 多环境配置支持 +3. **代码质量** - 统一规范、静态代码检查 + +### 低优先级技术债务 +1. **文档完善** - API文档、架构文档 +2. **测试覆盖** - 单元测试、集成测试 +3. **部署优化** - CI/CD流程完善 + +## 🔍 代码审查统计 + +### 审查文件分类 +| 文件类型 | 数量 | 问题数量 | 问题密度 | +|----------|------|----------|----------| +| Controller | 8个 | 12个 | 1.5/文件 | +| Service | 10个 | 8个 | 0.8/文件 | +| Entity | 7个 | 5个 | 0.7/文件 | +| Config | 6个 | 9个 | 1.5/文件 | +| Utils | 4个 | 3个 | 0.75/文件 | + +### 问题类型分布 +| 问题类型 | 数量 | 占比 | 严重程度 | +|----------|------|------|----------| +| 安全漏洞 | 6个 | 16% | 高危 | +| 代码规范 | 12个 | 32% | 中低危 | +| 性能问题 | 5个 | 13% | 中危 | +| 功能缺陷 | 8个 | 21% | 中危 | +| 维护性问题 | 7个 | 18% | 低危 | + +## 📈 改进效果预测 + +### 修复后的预期改进 + +| 改进项 | 当前状态 | 预期状态 | 改进幅度 | +|--------|----------|----------|----------| +| 安全评分 | 4/10 | 8/10 | +100% | +| 代码质量 | 6/10 | 8/10 | +33% | +| 性能评分 | 6/10 | 7.5/10 | +25% | +| 可维护性 | 7/10 | 8.5/10 | +21% | + +**预期总体评分提升**: 6.2 → 8.0 (+29%) + +## ✅ 审查结论 + +Freedom Admin 项目具备良好的基础架构设计和完整的功能实现,但在**安全性**方面存在严重缺陷,需要立即采取行动。项目的核心问题是**敏感信息管理和安全配置**,这些问题的修复将显著提升系统的整体安全水平。 + +**主要建议**: +1. **立即行动**: 优先处理P0和P1级别的安全问题 +2. **系统加固**: 建立完善的安全开发和部署流程 +3. **持续改进**: 建立代码审查和质量保障机制 + +通过系统的修复和优化,该项目有望达到生产环境的安全和质量标准。 + +--- + +**审查完成时间**: 2024年11月3日 +**审查人员**: 代码审查专家系统 +**下次审查建议**: 修复主要问题后3个月进行复查 \ No newline at end of file diff --git a/HELP.md b/HELP.md new file mode 100644 index 0000000..44fe3af --- /dev/null +++ b/HELP.md @@ -0,0 +1,26 @@ +# Getting Started + +### Reference Documentation + +For further reference, please consider the following sections: + +* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) +* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/) +* [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.4.1/maven-plugin/reference/html/#build-image) +* [Spring Security](https://docs.spring.io/spring-boot/docs/2.4.1/reference/htmlsingle/#boot-features-security) +* [Spring Web](https://docs.spring.io/spring-boot/docs/2.4.1/reference/htmlsingle/#boot-features-developing-web-applications) +* [Thymeleaf](https://docs.spring.io/spring-boot/docs/2.4.1/reference/htmlsingle/#boot-features-spring-mvc-template-engines) + +### Guides + +The following guides illustrate how to use some features concretely: + +* [Securing a Web Application](https://spring.io/guides/gs/securing-web/) +* [Spring Boot and OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2/) +* [Authenticating a User with LDAP](https://spring.io/guides/gs/authenticating-ldap/) +* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) +* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) +* [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/) +* [Accessing data with MySQL](https://spring.io/guides/gs/accessing-data-mysql/) +* [Handling Form Submission](https://spring.io/guides/gs/handling-form-submission/) + diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..df809f0 --- /dev/null +++ b/README.en.md @@ -0,0 +1,36 @@ +# freedom-admin + +#### Description +十分钟毕设-内部项目 + +#### Software Architecture +Software architecture description + +#### Installation + +1. xxxx +2. xxxx +3. xxxx + +#### Instructions + +1. xxxx +2. xxxx +3. xxxx + +#### Contribution + +1. Fork the repository +2. Create Feat_xxx branch +3. Commit your code +4. Create Pull Request + + +#### Gitee Feature + +1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md +2. Gitee blog [blog.gitee.com](https://blog.gitee.com) +3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) +4. The most valuable open source project [GVP](https://gitee.com/gvp) +5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) +6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md new file mode 100644 index 0000000..4b21fa3 --- /dev/null +++ b/README.md @@ -0,0 +1,226 @@ +# Freedom Admin 管理系统 + +## 项目简介 + +Freedom Admin 是一个基于 Spring Boot 2.7.10 的后台管理系统,集成了 Spring Security、MyBatis-Plus、Thymeleaf 等主流技术栈,提供完整的代码生成器、邮件发送、用户管理等功能。 + +## 技术栈 + +- **后端框架**: Spring Boot 2.7.10 +- **安全框架**: Spring Security 3.3.2 +- **ORM 框架**: MyBatis-Plus 3.5.7 +- **模板引擎**: Thymeleaf +- **数据库**: MySQL 8.0.33 +- **连接池**: HikariCP +- **权限管理**: JWT 认证 +- **邮件服务**: Spring Mail +- **代码生成**: Beetl 模板引擎 + +## 项目结构 + +``` +├── src/main/java/com/freedom/admin/ +│ ├── AdminApplication.java # 应用启动类 +│ ├── base/ # 基础类 +│ ├── common/ # 通用工具类 +│ ├── config/ # 配置类 +│ ├── controller/ # 控制器层 +│ ├── entity/ # 实体类 +│ ├── mapper/ # 数据访问层 +│ ├── service/ # 服务层 +│ └── util/ # 工具类 +├── src/main/resources/ +│ ├── mapper/ # MyBatis 映射文件 +│ ├── btl/ # 代码生成模板 +│ └── application.yml # 配置文件 +├── sql/ +│ └── init.sql # 数据库初始化脚本 +└── pom.xml # Maven 依赖配置 +``` + +## 功能特性 + +### 1. 用户管理 +- 用户注册、登录、权限管理 +- JWT 认证机制 +- 用户信息维护 + +### 2. 代码生成器 +- 基于 Beetl 模板引擎 +- 支持前后端代码自动生成 +- 可自定义生成规则和模板 +- 生成 Controller、Service、Mapper、Entity、Vue 等文件 + +### 3. 邮件服务 +- 邮件验证码发送 +- 注册邮件通知 +- 可配置的邮件模板 + +### 4. 数据管理 +- 系统部门管理 +- 活动信息管理 +- 药品信息管理 +- 订单信息管理 +- 病例本管理 + +### 5. 系统特性 +- 统一异常处理 +- 分页插件支持 +- 逻辑删除 +- 接口权限控制 + +## 快速开始 + +### 环境要求 + +- JDK 1.8+ +- Maven 3.6.3+ +- MySQL 8.0+ + +### 数据库配置 + +1. 创建数据库: +```sql +CREATE DATABASE demo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +``` + +2. 执行初始化脚本: +```bash +mysql -u root -p demo < sql/init.sql +``` + +### 配置文件 + +修改 `src/main/resources/application.yml` 中的数据库连接信息: + +```yaml +spring: + datasource: + url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC + username: root + password: 123456 +``` + +### 运行项目 + +```bash +# 使用 Maven 运行 +mvn spring-boot:run + +# 或者打包后运行 +mvn clean package +java -jar target/freedom-admin-1.0.1.jar +``` + +项目启动后访问:http://localhost:8800 + +## 代码生成器使用 + +### 1. 查询数据库表 + +```bash +GET /generator/findListWithPage?pageNum=1&pageSize=10 +``` + +### 2. 获取表下拉列表 + +```bash +GET /generator/getTableSelectList +``` + +### 3. 执行代码生成 + +```bash +POST /generator/execute +Content-Type: application/x-www-form-urlencoded + +tableName=your_table_name&ruleStr=[{"rule":"value"}] +``` + +## API 接口 + +### 认证相关 +- 用户登录 +- Token 刷新 +- 权限验证 + +### 用户管理 +- 用户注册 +- 用户信息查询 +- 用户信息修改 + +### 邮件服务 +- 发送验证码 +- 注册邮件通知 + +### 数据管理 +- 部门管理 +- 活动管理 +- 药品管理 +- 订单管理 + +## 开发指南 + +### 添加新功能模块 + +1. 在 `entity` 包下创建实体类 +2. 在 `mapper` 包下创建 Mapper 接口 +3. 在 `service` 包下创建 Service 接口和实现类 +4. 在 `controller` 包下创建 Controller 类 +5. 在 `resources/mapper` 下创建对应的 XML 映射文件 + +### 权限配置 + +在 Controller 方法上使用注解控制权限: + +```java +@PermitAll // 允许所有用户访问 +@PreAuthorize("hasRole('ADMIN')") // 需要管理员权限 +``` + +## 部署说明 + +### 生产环境配置 + +1. 修改数据库连接为生产环境地址 +2. 配置 JWT 密钥 +3. 配置邮件服务器信息 +4. 设置日志级别和路径 + +### 安全建议 + +- 定期更换 JWT 密钥 +- 使用 HTTPS 协议 +- 配置防火墙规则 +- 定期备份数据库 + +## 常见问题 + +### Q: 项目启动失败,提示数据库连接错误 +A: 检查 application.yml 中的数据库配置是否正确,确保数据库服务已启动 + +### Q: 代码生成器无法生成文件 +A: 检查模板文件路径和项目路径配置,确保有写入权限 + +### Q: 邮件发送失败 +A: 检查邮件服务器配置,包括 SMTP 主机、端口、用户名和授权码 + +## 贡献指南 + +1. Fork 本仓库 +2. 创建新的功能分支 +3. 提交代码变更 +4. 创建 Pull Request + +## 许可证 + +本项目仅用于内部学习和研究使用。 + +## 联系方式 + +- 项目作者: zhangjiaxun +- 邮箱: 732128096@qq.com + +--- + +*项目最后更新: 2024年4月* \ No newline at end of file diff --git a/SQLER图.md b/SQLER图.md new file mode 100644 index 0000000..e722427 --- /dev/null +++ b/SQLER图.md @@ -0,0 +1,379 @@ +# Freedom Admin 管理系统 SQLER 图 + +## 数据库关系图 + +```mermaid +erDiagram + %% 用户表 + TSysUser { + bigint id PK + varchar username + varchar password + varchar encodeType + varchar userType + varchar nickname + varchar avatar + varchar phone + varchar email + varchar signature + int status + varchar createBy + datetime createDate + varchar updateBy + datetime updateDate + boolean delFlag + } + + %% 角色表 + TSysRole { + bigint id PK + varchar roleName + varchar roleCode + varchar description + int status + varchar createBy + datetime createDate + varchar updateBy + datetime updateDate + boolean delFlag + } + + %% 权限表 + TSysPermission { + bigint id PK + varchar permissionName + varchar permissionCode + varchar description + varchar url + varchar method + int type + bigint parentId + int sort + int status + varchar createBy + datetime createDate + varchar updateBy + datetime updateDate + boolean delFlag + } + + %% 部门表 + TSysDept { + bigint id PK + varchar deptName + varchar deptCode + varchar description + bigint parentId + int sort + int status + varchar createBy + datetime createDate + varchar updateBy + datetime updateDate + boolean delFlag + } + + %% 关联表 + TSysUserRole { + bigint id PK + bigint userId FK + bigint roleId FK + varchar createBy + datetime createDate + varchar updateBy + datetime updateDate + boolean delFlag + } + + TSysRolePermission { + bigint id PK + bigint roleId FK + bigint permissionId FK + varchar createBy + datetime createDate + varchar updateBy + datetime updateDate + boolean delFlag + } + + %% 生成器配置表 + GeneratorConfig { + bigint id PK + varchar tableName + varchar tableComment + varchar author + varchar packageName + varchar moduleName + varchar functionName + varchar entityName + varchar mapperName + varchar serviceName + varchar serviceImplName + varchar controllerName + varchar createBy + datetime createDate + varchar updateBy + datetime updateDate + boolean delFlag + } + + %% 关系定义 + TSysUser ||--o{ TSysUserRole : "拥有" + TSysRole ||--o{ TSysUserRole : "分配" + TSysRole ||--o{ TSysRolePermission : "授权" + TSysPermission ||--o{ TSysRolePermission : "被授予" + TSysDept ||--o{ TSysUser : "属于" + TSysPermission ||--o{ TSysPermission : "层级关系" + TSysDept ||--o{ TSysDept : "层级关系" +``` + +## 数据字典 + +### 表结构详细信息 + +#### 1. 用户表 (t_sys_user) + +| 字段名 | 数据类型 | 长度 | 允许空 | 主键 | 说明 | 默认值 | +|--------|----------|------|--------|------|------|--------| +| id | bigint | 20 | NO | PK | 主键ID | 自增 | +| username | varchar | 50 | NO | | 登录用户名 | | +| password | varchar | 255 | NO | | 加密密码 | | +| encodeType | varchar | 20 | YES | | 加密方式 | MD5 | +| userType | varchar | 20 | YES | | 用户类型 | | +| nickname | varchar | 50 | YES | | 用户昵称 | | +| avatar | varchar | 255 | YES | | 头像URL | | +| phone | varchar | 20 | YES | | 手机号 | | +| email | varchar | 100 | YES | | 邮箱 | | +| signature | varchar | 255 | YES | | 个性签名 | | +| status | int | 11 | YES | | 状态(0禁用,1启用) | 1 | +| createBy | varchar | 50 | YES | | 创建人 | | +| createDate | datetime | | YES | | 创建时间 | | +| updateBy | varchar | 50 | YES | | 更新人 | | +| updateDate | datetime | | YES | | 更新时间 | | +| delFlag | boolean | | YES | | 删除标记 | 0 | + +#### 2. 角色表 (t_sys_role) + +| 字段名 | 数据类型 | 长度 | 允许空 | 主键 | 说明 | 默认值 | +|--------|----------|------|--------|------|------|--------| +| id | bigint | 20 | NO | PK | 主键ID | 自增 | +| roleName | varchar | 50 | NO | | 角色名称 | | +| roleCode | varchar | 50 | NO | | 角色编码 | | +| description | varchar | 255 | YES | | 角色描述 | | +| status | int | 11 | YES | | 状态(0禁用,1启用) | 1 | +| createBy | varchar | 50 | YES | | 创建人 | | +| createDate | datetime | | YES | | 创建时间 | | +| updateBy | varchar | 50 | YES | | 更新人 | | +| updateDate | datetime | | YES | | 更新时间 | | +| delFlag | boolean | | YES | | 删除标记 | 0 | + +#### 3. 权限表 (t_sys_permission) + +| 字段名 | 数据类型 | 长度 | 允许空 | 主键 | 说明 | 默认值 | +|--------|----------|------|--------|------|------|--------| +| id | bigint | 20 | NO | PK | 主键ID | 自增 | +| permissionName | varchar | 50 | NO | | 权限名称 | | +| permissionCode | varchar | 50 | NO | | 权限编码 | | +| description | varchar | 255 | YES | | 权限描述 | | +| url | varchar | 255 | YES | | 请求URL | | +| method | varchar | 10 | YES | | 请求方法 | | +| type | int | 11 | YES | | 权限类型(1菜单,2按钮) | | +| parentId | bigint | 20 | YES | | 父级权限ID | 0 | +| sort | int | 11 | YES | | 排序号 | 0 | +| status | int | 11 | YES | | 状态(0禁用,1启用) | 1 | +| createBy | varchar | 50 | YES | | 创建人 | | +| createDate | datetime | | YES | | 创建时间 | | +| updateBy | varchar | 50 | YES | | 更新人 | | +| updateDate | datetime | | YES | | 更新时间 | | +| delFlag | boolean | | YES | | 删除标记 | 0 | + +#### 4. 部门表 (t_sys_dept) + +| 字段名 | 数据类型 | 长度 | 允许空 | 主键 | 说明 | 默认值 | +|--------|----------|------|--------|------|------|--------| +| id | bigint | 20 | NO | PK | 主键ID | 自增 | +| deptName | varchar | 50 | NO | | 部门名称 | | +| deptCode | varchar | 50 | NO | | 部门编码 | | +| description | varchar | 255 | YES | | 部门描述 | | +| parentId | bigint | 20 | YES | | 父级部门ID | 0 | +| sort | int | 11 | YES | | 排序号 | 0 | +| status | int | 11 | YES | | 状态(0禁用,1启用) | 1 | +| createBy | varchar | 50 | YES | | 创建人 | | +| createDate | datetime | | YES | | 创建时间 | | +| updateBy | varchar | 50 | YES | | 更新人 | | +| updateDate | datetime | | YES | | 更新时间 | | +| delFlag | boolean | | YES | | 删除标记 | 0 | + +#### 5. 用户角色关联表 (t_sys_user_role) + +| 字段名 | 数据类型 | 长度 | 允许空 | 主键 | 说明 | 默认值 | +|--------|----------|------|--------|------|------|--------| +| id | bigint | 20 | NO | PK | 主键ID | 自增 | +| userId | bigint | 20 | NO | FK | 用户ID | | +| roleId | bigint | 20 | NO | FK | 角色ID | | +| createBy | varchar | 50 | YES | | 创建人 | | +| createDate | datetime | | YES | | 创建时间 | | +| updateBy | varchar | 50 | YES | | 更新人 | | +| updateDate | datetime | | YES | | 更新时间 | | +| delFlag | boolean | | YES | | 删除标记 | 0 | + +#### 6. 角色权限关联表 (t_sys_role_permission) + +| 字段名 | 数据类型 | 长度 | 允许空 | 主键 | 说明 | 默认值 | +|--------|----------|------|--------|------|------|--------| +| id | bigint | 20 | NO | PK | 主键ID | 自增 | +| roleId | bigint | 20 | NO | FK | 角色ID | | +| permissionId | bigint | 20 | NO | FK | 权限ID | | +| createBy | varchar | 50 | YES | | 创建人 | | +| createDate | datetime | | YES | | 创建时间 | | +| updateBy | varchar | 50 | YES | | 更新人 | | +| updateDate | datetime | | YES | | 更新时间 | | +| delFlag | boolean | | YES | | 删除标记 | 0 | + +## 索引设计 + +### 主键索引 +```sql +-- 所有表的主键索引 +PRIMARY KEY (id) +``` + +### 唯一索引 +```sql +-- 用户表唯一索引 +UNIQUE KEY uk_username (username), +UNIQUE KEY uk_phone (phone), +UNIQUE KEY uk_email (email) + +-- 角色表唯一索引 +UNIQUE KEY uk_role_code (roleCode) + +-- 权限表唯一索引 +UNIQUE KEY uk_permission_code (permissionCode) + +-- 部门表唯一索引 +UNIQUE KEY uk_dept_code (deptCode) +``` + +### 外键索引 +```sql +-- 用户角色关联表外键索引 +KEY idx_user_id (userId), +KEY idx_role_id (roleId) + +-- 角色权限关联表外键索引 +KEY idx_role_id (roleId), +KEY idx_permission_id (permissionId) + +-- 权限表层级索引 +KEY idx_parent_id (parentId) + +-- 部门表层级索引 +KEY idx_parent_id (parentId) +``` + +## 数据关系说明 + +### 1. 用户-角色-权限关系模型 + +```mermaid +graph TB + U[用户] --> UR[用户角色] + R[角色] --> UR + R --> RP[角色权限] + P[权限] --> RP + + subgraph "基础实体" + U + R + P + D[部门] + end + + subgraph "关联关系" + UR + RP + end + + D --> U +``` + +### 2. 权限层级结构 + +```mermaid +graph TD + A[根权限] --> B[模块权限] + B --> C[菜单权限] + C --> D[按钮权限] + + B --> E[模块权限2] + E --> F[菜单权限2] + F --> G[按钮权限2] +``` + +### 3. 部门层级结构 + +```mermaid +graph TD + A[总公司] --> B[分公司1] + A --> C[分公司2] + + B --> D[部门A] + B --> E[部门B] + + C --> F[部门C] + C --> G[部门D] +``` + +## 查询优化建议 + +### 1. 常用查询SQL + +```sql +-- 查询用户权限 +SELECT p.* FROM t_sys_user u +JOIN t_sys_user_role ur ON u.id = ur.user_id +JOIN t_sys_role r ON ur.role_id = r.id +JOIN t_sys_role_permission rp ON r.id = rp.role_id +JOIN t_sys_permission p ON rp.permission_id = p.id +WHERE u.id = ? AND u.del_flag = 0 AND r.del_flag = 0 AND p.del_flag = 0 + +-- 查询部门层级 +WITH RECURSIVE dept_tree AS ( + SELECT * FROM t_sys_dept WHERE id = ? + UNION ALL + SELECT d.* FROM t_sys_dept d + JOIN dept_tree dt ON d.parent_id = dt.id +) +SELECT * FROM dept_tree WHERE del_flag = 0 +``` + +### 2. 性能优化建议 + +1. **索引优化**:为频繁查询的字段建立合适索引 +2. **分页查询**:使用MyBatis-Plus分页插件 +3. **逻辑删除**:使用del_flag字段进行软删除 +4. **数据归档**:定期归档历史数据 + +## 数据完整性约束 + +### 1. 实体完整性 +- 所有表主键ID唯一且非空 +- 通过自增方式保证主键唯一性 + +### 2. 参照完整性 +- 用户角色关联表引用用户表和角色表 +- 角色权限关联表引用角色表和权限表 +- 外键约束确保数据一致性 + +### 3. 域完整性 +- 状态字段使用枚举值约束 +- 时间字段使用标准datetime格式 +- 字符字段长度限制防止溢出 + +### 4. 业务完整性 +- 逻辑删除保证数据可恢复 +- 创建时间和更新时间的自动维护 +- 创建人和更新人的记录追踪 + +这个SQLER图详细描述了 Freedom Admin 管理系统的数据库结构设计,包括表关系、字段定义、索引设计和数据完整性约束。 \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..a16b543 --- /dev/null +++ b/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..c8d4337 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8f0277f --- /dev/null +++ b/pom.xml @@ -0,0 +1,180 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.10 + + + com.freedom + freedom-admin + 1.0.1 + freedom-admin + freedom-admin + + + 1.8 + 3.6.3 + 3.1.2.RELEASE + 1.2.83 + 3.11 + 8.0.33 + + + + + org.springframework.boot + spring-boot-starter-security + 3.3.2 + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity5 + ${thymeleaf-extras-springsecurity5.version} + + + mysql + mysql-connector-java + ${mysql-connector-java.version} + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + + org.springframework.boot + spring-boot-starter-mail + + + + com.baomidou + mybatis-plus-boot-starter + 3.5.7 + + + + com.github.pagehelper + pagehelper-spring-boot-starter + 2.1.0 + + + org.mybatis + mybatis + + + org.mybatis + mybatis-spring + + + + + + io.jsonwebtoken + jjwt + 0.9.1 + + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + + org.apache.commons + commons-collections4 + 4.4 + + + + com.ibeetl + beetl + 3.16.2.RELEASE + + + + com.google.guava + guava + 32.1.2-jre + + + + com.alibaba + fastjson + ${fastjson.version} + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 1.4.1 + + + enforce + + enforce + + + + + ${maven.version} + + + ${java.version} + + + + org.jboss.netty + org.codehaus.groovy + + true + + + + + + + + + + + diff --git a/sql/init.sql b/sql/init.sql new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/com/freedom/admin/AdminApplication.java b/src/main/java/com/freedom/admin/AdminApplication.java new file mode 100644 index 0000000..0be132b --- /dev/null +++ b/src/main/java/com/freedom/admin/AdminApplication.java @@ -0,0 +1,20 @@ +package com.freedom.admin; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author zhangjiaxun + * @date 2021/2/8 15:39 + * @description 系统启动类 + */ +@SpringBootApplication +@MapperScan("com.freedom.admin.mapper") +public class AdminApplication { + + public static void main(String[] args) { + SpringApplication.run(AdminApplication.class, args); + } + +} diff --git a/src/main/java/com/freedom/admin/base/BaseController.java b/src/main/java/com/freedom/admin/base/BaseController.java new file mode 100644 index 0000000..3820503 --- /dev/null +++ b/src/main/java/com/freedom/admin/base/BaseController.java @@ -0,0 +1,53 @@ +package com.freedom.admin.base; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.freedom.admin.entity.TSysUser; +import com.freedom.admin.service.TSysUserService; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * @author zhangjiaxun + * @date 2021/2/2 17:47 + * @description + */ +@Log4j2 +public class BaseController { + + @Autowired + private TSysUserService tSysUserService; + + /** + * 获取当前登录用户账号 + * */ + public String getUsername() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if(!authentication.isAuthenticated()){ + return null; + } + UserDetails userDetails = (UserDetails)authentication.getPrincipal(); + return userDetails.getUsername(); + } + + /** + * 获取当前用户信息 + * */ + public TSysUser getUser() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("username",getUsername()); + return tSysUserService.getOne(queryWrapper); + } + + /** + * 获取当前用户ID + * */ + public Long getUserId() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("username",getUsername()); + return tSysUserService.getOne(queryWrapper).getId(); + } + +} diff --git a/src/main/java/com/freedom/admin/common/Result.java b/src/main/java/com/freedom/admin/common/Result.java new file mode 100644 index 0000000..bdb8cbf --- /dev/null +++ b/src/main/java/com/freedom/admin/common/Result.java @@ -0,0 +1,99 @@ +package com.freedom.admin.common; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author zhangjiaxun + * @date 2021/1/27 13:55 + * @description 统一返回结果的类 + */ +@Data +public class Result implements Serializable { + + /** + * 是否成功 + * */ + private Boolean success; + + /** + * 返回码 + * */ + private Integer code; + + /** + * 返回消息 + * */ + private String message; + + /** + * 总数 + * */ + private Integer count; + + /** + * 返回数据 + * */ + private T data; + + /** + * token + * */ + private String token; + + /** + * 构造方法私有 + * */ + private Result() {} + + /** + * 成功静态方法 + * */ + public static Result ok() { + Result r = new Result(); + r.setSuccess(true); + r.setCode(ResultCode.SUCCESS.getCode()); + r.setMessage(ResultCode.SUCCESS.getDesc()); + return r; + } + + /** + * 失败静态方法 + * */ + public static Result error() { + Result r = new Result(); + r.setSuccess(false); + r.setCode(ResultCode.ERROR.getCode()); + r.setMessage(ResultCode.ERROR.getDesc()); + return r; + } + + /** + * 设置message + * */ + public Result message(String message){ + this.setMessage(message); + return this; + } + + /** + * 设置code + * */ + public Result code(Integer code) { + this.setCode(code); + return this; + } + + public Result token(String token){ + this.token = token; + return this; + } + + public Result setData(T data) { + this.data = data; + return this; + } + + +} diff --git a/src/main/java/com/freedom/admin/common/ResultCode.java b/src/main/java/com/freedom/admin/common/ResultCode.java new file mode 100644 index 0000000..f527789 --- /dev/null +++ b/src/main/java/com/freedom/admin/common/ResultCode.java @@ -0,0 +1,26 @@ +package com.freedom.admin.common; + +import lombok.Getter; + +/** + * @author zhangjiaxun + * @date 2021/1/27 14:01 + * @description 返回码值 + */ +@Getter +public enum ResultCode { + + SUCCESS(200,"请求成功"), + ERROR(500,"请求失败") + ; + + private Integer code; + + private String desc; + + ResultCode(Integer code, String desc) { + this.code = code; + this.desc = desc; + } + +} diff --git a/src/main/java/com/freedom/admin/config/GlobalExceptionHandler.java b/src/main/java/com/freedom/admin/config/GlobalExceptionHandler.java new file mode 100644 index 0000000..e71daba --- /dev/null +++ b/src/main/java/com/freedom/admin/config/GlobalExceptionHandler.java @@ -0,0 +1,39 @@ +package com.freedom.admin.config; + +import com.freedom.admin.common.Result; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * @author zhangjiaxun + * @date 2021/1/27 14:19 + * @description 全局异常处理 + */ +@ControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + /** + * 指定处理Exception.class异常 + * */ + @ExceptionHandler(Exception.class) + @ResponseBody + public Result error(Exception e){ + e.printStackTrace(); + return Result.error().message("执行了全局异常"); + } + + /** + * 指定处理MyException.class异常 + * */ + @ExceptionHandler(MyException.class) + @ResponseBody + public Result error(MyException e){ + log.error(e.getMessage()); + e.printStackTrace(); + return Result.error().code(e.getCode()).message(e.getMessage()); + } + +} diff --git a/src/main/java/com/freedom/admin/config/JwtAuthenticationTokenFilter.java b/src/main/java/com/freedom/admin/config/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000..57787cb --- /dev/null +++ b/src/main/java/com/freedom/admin/config/JwtAuthenticationTokenFilter.java @@ -0,0 +1,61 @@ +package com.freedom.admin.config; + +import com.freedom.admin.service.UserService; +import com.freedom.admin.utils.JwtUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author zhangjiaxun + * @date 2021/2/1 10:35 + * @description 继承OncePerRequestFilter以确保请求只被此过滤器执行一次 + */ +@Component +@Slf4j +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { + + @Value("${jwt.header}") + private String header; + @Autowired + private UserService userService; + @Autowired + private JwtUtils jwtUtils; + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + log.info("==============当前的请求路径url【{}】",request.getRequestURL()); + // token + String token = request.getHeader(header); + if (token !=null){ + // 获取username + String username = jwtUtils.getUsernameFormToken(token); + // 当username不为空且spring-security用户认证信息为空,进行认证 + if (username !=null && SecurityContextHolder.getContext().getAuthentication() ==null){ + UserDetails userDetails = userService.loadUserByUsername(username); + if (userDetails != null) { + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } + } + // 调用下一个过滤器,沿着过滤器链 + filterChain.doFilter(request,response); + } + +} diff --git a/src/main/java/com/freedom/admin/config/MyAuthenticationFailureHandler.java b/src/main/java/com/freedom/admin/config/MyAuthenticationFailureHandler.java new file mode 100644 index 0000000..5d73cd9 --- /dev/null +++ b/src/main/java/com/freedom/admin/config/MyAuthenticationFailureHandler.java @@ -0,0 +1,35 @@ +package com.freedom.admin.config; + +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author zhangjiaxun + * @date 2021/1/5 17:40 + * @description 对安全验证失败的请求进行响应处理 + */ +@Component +public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler { + + @Override + public void onAuthenticationFailure(HttpServletRequest request, + HttpServletResponse response, + AuthenticationException e) throws IOException { + //修改编码格式 + request.setCharacterEncoding("utf-8"); + response.setContentType("application/json"); + + if (e instanceof BadCredentialsException){ + response.getWriter().write("用户名或密码错误"); + }else { + response.getWriter().write("登录失败!"); + } + + } +} diff --git a/src/main/java/com/freedom/admin/config/MyAuthenticationSuccessHandler.java b/src/main/java/com/freedom/admin/config/MyAuthenticationSuccessHandler.java new file mode 100644 index 0000000..d533664 --- /dev/null +++ b/src/main/java/com/freedom/admin/config/MyAuthenticationSuccessHandler.java @@ -0,0 +1,44 @@ +package com.freedom.admin.config; + +import com.alibaba.fastjson.JSON; +import com.freedom.admin.common.Result; +import com.freedom.admin.utils.JwtUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author zhangjiaxun + * @date 2021/1/5 17:40 + * @description 对安全验证成功的请求进行响应处理 + */ +@Component +@Slf4j +public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler { + + @Autowired + private JwtUtils jwtUtils; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { + + UserDetails userDetails = (UserDetails)authentication.getPrincipal(); + // 生成token + String token = jwtUtils.generateToken(userDetails.getUsername()); + Result result = Result.ok().message("登录成功").token(token); + // 修改编码格式 + response.setCharacterEncoding("utf-8"); + response.setContentType("application/json"); + // 输出结果 + response.getWriter().write(JSON.toJSONString(result)); + } + +} diff --git a/src/main/java/com/freedom/admin/config/MyException.java b/src/main/java/com/freedom/admin/config/MyException.java new file mode 100644 index 0000000..05c6293 --- /dev/null +++ b/src/main/java/com/freedom/admin/config/MyException.java @@ -0,0 +1,27 @@ +package com.freedom.admin.config; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author zhangjiaxun + * @date 2021/1/27 14:25 + * @description 自定义异常 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MyException extends RuntimeException { + + /** + * 状态码 + * */ + private Integer code; + + /** + * 状态码 + * */ + private String message; + +} diff --git a/src/main/java/com/freedom/admin/config/RestAuthenticationEntryPoint.java b/src/main/java/com/freedom/admin/config/RestAuthenticationEntryPoint.java new file mode 100644 index 0000000..d2a5e6c --- /dev/null +++ b/src/main/java/com/freedom/admin/config/RestAuthenticationEntryPoint.java @@ -0,0 +1,29 @@ +package com.freedom.admin.config; + +import com.alibaba.fastjson.JSON; +import com.freedom.admin.common.Result; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author zhangjiaxun + * @date 2021/2/1 14:03 + * @description 当未登录或者token失效访问接口时,自定义的返回结果 + */ +@Component +public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException { + response.setCharacterEncoding("UTF-8"); + response.setContentType("application/json"); + response.getWriter().println(JSON.toJSONString(Result.error().message("尚未登录,或者登录过期 " + e.getMessage()))); + response.getWriter().flush(); + } + +} diff --git a/src/main/java/com/freedom/admin/config/WebSecurityConfig.java b/src/main/java/com/freedom/admin/config/WebSecurityConfig.java new file mode 100644 index 0000000..c7d0196 --- /dev/null +++ b/src/main/java/com/freedom/admin/config/WebSecurityConfig.java @@ -0,0 +1,124 @@ +package com.freedom.admin.config; + +import com.freedom.admin.service.impl.UserServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; + +/** + * @author zhangjiaxun + * @date 2021/1/5 17:40 + * @description + */ +@Configuration +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + /** + * 无权限拦截器 + */ + @Autowired + private RestAuthenticationEntryPoint restAuthenticationEntryPoint; + + /** + * 登录成功逻辑 + */ + @Autowired + private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; + + /** + * 登录失败逻辑 + */ + @Autowired + private MyAuthenticationFailureHandler myAuthenticationFailureHandler; + + /** + * 继承OncePerRequestFilter以确保请求只被此过滤器执行一次 + */ + @Autowired + private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; + + @Autowired + UserServiceImpl userDetailsService; + + @Override + public void configure(WebSecurity web) throws Exception { + // 放行静态资源 + web.ignoring().antMatchers("/login.html", "/pear.config.json", + "/admin/css/**","/admin/images/**", + "/component/code/**","/component/layui/**","/component/pear/**"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.cors().and() + .csrf().disable() + .sessionManagement() + // 基于token,所以不需要session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .httpBasic() + .authenticationEntryPoint(restAuthenticationEntryPoint) + .and() + .authorizeRequests()// 验证码允许匿名访问 + .antMatchers("/email-sender/sendEmailVerificationCode","/sys-user/register") + .anonymous() + .and() + .formLogin() + .loginProcessingUrl("/login") + .successHandler(myAuthenticationSuccessHandler) + .failureHandler(myAuthenticationFailureHandler) + .usernameParameter("username") + .passwordParameter("password") + .and() + .logout() + .logoutUrl("/logout") + .and() + .rememberMe() + .rememberMeParameter("rememberme") + .and() + .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); + } + + /** + * 跨越配置 + */ + @Bean + public CorsConfigurationSource corsConfigurationSource() { + return httpServletRequest -> { + CorsConfiguration cfg = new CorsConfiguration(); + cfg.addAllowedHeader("*"); + cfg.addAllowedMethod("*"); + // 设置允许跨域请求的域名 + cfg.addAllowedOrigin("http://localhost:8080"); + // 允许任何请求 + cfg.addAllowedHeader("*"); + cfg.addExposedHeader("token"); + // 是否允许证书 + cfg.setAllowCredentials(true); + // + cfg.checkOrigin("http://localhost:8080"); + return cfg; + }; + } + +} diff --git a/src/main/java/com/freedom/admin/controller/GeneratorController.java b/src/main/java/com/freedom/admin/controller/GeneratorController.java new file mode 100644 index 0000000..83873d9 --- /dev/null +++ b/src/main/java/com/freedom/admin/controller/GeneratorController.java @@ -0,0 +1,541 @@ +package com.freedom.admin.controller; + +import com.alibaba.fastjson.JSON; +import com.freedom.admin.base.BaseController; +import com.freedom.admin.common.Result; +import com.freedom.admin.entity.GeneratorConfig; +import com.freedom.admin.entity.GeneratorTableVO; +import com.freedom.admin.service.GeneratorService; +import com.github.pagehelper.PageInfo; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.annotations.Param; +import org.beetl.core.Configuration; +import org.beetl.core.GroupTemplate; +import org.beetl.core.Template; +import org.beetl.core.resource.ClasspathResourceLoader; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.security.PermitAll; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @description: TODO + * @author: zhangjiaxun + * @date: 2021年03月20日 20:05:00 + */ +@RestController +@RequestMapping("generator") +public class GeneratorController extends BaseController { + + + @Autowired + private GeneratorService generatorService; + + /** + * 列表查询 + * + * @param pageNum 当前页 + * @param pageSize 页数 + * @return Result + */ + @RequestMapping("findListWithPage") + @PermitAll + public Result findListWithPage(@Param("pageNum") int pageNum, + @Param("pageSize") int pageSize, + GeneratorTableVO generatorTableVO) { + PageInfo pageInfo = generatorService.getTableListWithPage(pageNum, pageSize, generatorTableVO); + return Result.ok().setData(pageInfo); + } + + /** + * 获取table下拉列表数据 + * + * @return Result + */ + @RequestMapping("getTableSelectList") + @PermitAll + public Result getTableSelectList() { + List> tableSelectList = generatorService.getTableSelectList(); + return Result.ok().setData(tableSelectList); + } + + /** + * 执行代码生成操作 + * @param tableName + * @param ruleStr + * @return + */ + @RequestMapping("execute") + @PermitAll + public Result execute(String tableName, String ruleStr) { + // 转换 + List> ruleList = (List>) JSON.parse(ruleStr); + + //模板路径 + ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader("/btl/"); + Configuration cfg = null; + try { + cfg = Configuration.defaultConfiguration(); + } catch (IOException e) { + e.printStackTrace(); + } + GroupTemplate gt = new GroupTemplate(resourceLoader, cfg); + // 生成代码 + Map result = null; + try { + result = generateCode(gt, tableName, ruleList); + } catch (Exception e) { + e.printStackTrace(); + } + return Result.ok().setData(result); + } + + /** + * 生成代码 + * + * @param gt + * @throws IOException + */ + private Map generateCode(GroupTemplate gt, + String tableName, + List> ruleList) throws Exception { + GeneratorConfig generatorConfig = new GeneratorConfig(); + // 前端代码文件生成配置 + generatorConfig.setFrontProjectDir("D:/workspace-sfzbs/freedom-web"); + generatorConfig.setViewParentDir("info"); + // 后端代码文件生成配置 + generatorConfig.setAfterProjectDir("D:/workspace-sfzbs/freedom-admin"); + generatorConfig.setMapperPackage("com.freedom.admin.mapper"); + generatorConfig.setEntityPackage("com.freedom.admin.entity"); + generatorConfig.setServicePackage("com.freedom.admin.service"); + generatorConfig.setServiceImplPackage("com.freedom.admin.service.impl"); + generatorConfig.setControllerPackage("com.freedom.admin.controller"); + generatorConfig.setBasePackage("com.freedom.admin.base"); + generatorConfig.setAuthor("zhangjiaxun"); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd hh:mm"); + generatorConfig.setDate(simpleDateFormat.toString()); + // 选用模板 + generatorConfig.setUsingTemplate("apiJsTemplate,viewTemplate,mapperXmlTemplate,mapperTemplate,entityTemplate,serviceTemplate,serviceImplTemplate,controllerTemplate"); + + generatorConfig.setTableName(tableName); + generatorConfig.setClassName(underlineToCamel(tableName,false)); + generatorConfig.setClassNameLowerCase(underlineToCamel(tableName,true)); + generatorConfig.setResourceName(underlineToHyphen(tableName)); + + Map result = new HashMap<>(); + if (generatorConfig.getUsingTemplate().contains("apiJsTemplate")){ + Template apiJsTemplate = gt.getTemplate("apiJs.btl"); + result.put(".js",generateApiJs(apiJsTemplate, generatorConfig, ruleList)); + } + + if (generatorConfig.getUsingTemplate().contains("viewTemplate")){ + Template viewTemplate = gt.getTemplate("view.btl"); + result.put(".vue",generateView(viewTemplate, generatorConfig, ruleList)); + } + + if (generatorConfig.getUsingTemplate().contains("mapperXmlTemplate")){ + Template mapperXmlTemplate = gt.getTemplate("mapperXml.btl"); + result.put("mapper.xml",generateMapperXml(mapperXmlTemplate, generatorConfig, ruleList)); + } + + if (generatorConfig.getUsingTemplate().contains("mapperTemplate")){ + Template mapperTemplate = gt.getTemplate("mapper.btl"); + result.put("mapper.java",generateMapper(mapperTemplate, generatorConfig)); + } + + if (generatorConfig.getUsingTemplate().contains("entityTemplate")){ + Template entityTemplate= gt.getTemplate("entity.btl"); + result.put("entity.java",generateEntity(entityTemplate, generatorConfig, ruleList)); + } + + if (generatorConfig.getUsingTemplate().contains("serviceTemplate")){ + Template serviceTemplate = gt.getTemplate("service.btl"); + result.put("service.java",generateService(serviceTemplate, generatorConfig)); + } + + if (generatorConfig.getUsingTemplate().contains("serviceImplTemplate")){ + Template serviceImplTemplate = gt.getTemplate("serviceImpl.btl"); + result.put("serviceImpl.java",generateServiceImpl(serviceImplTemplate, generatorConfig)); + } + + if (generatorConfig.getUsingTemplate().contains("controllerTemplate")){ + Template controllerTemplate = gt.getTemplate("controller.btl"); + result.put("controller.java",generateController(controllerTemplate, generatorConfig)); + } + + return result; + } + + /** + * 代码生成:generateApiJs + */ + private String generateApiJs(Template apiJsTemplate, + GeneratorConfig generatorConfig, + List> ruleList) throws IOException { + OutputStream out = null; + + //生成mapper代码 + apiJsTemplate.binding("generatorConfig", generatorConfig); + apiJsTemplate.binding("ruleList", ruleList); + String apiJsResult = apiJsTemplate.render(); + + //创建文件 + String apiJsFileUrl = generatorConfig.getFrontProjectDir() + "/src/api/"+ generatorConfig.getViewParentDir() +"/" + generatorConfig.getResourceName() + ".js"; + File apiJsFile = new File(apiJsFileUrl); + File apiJsDir = apiJsFile.getParentFile(); + if (!apiJsDir.exists()) { + apiJsDir.mkdirs(); + } + if (apiJsFile.exists()) { + apiJsFile.delete(); + } + if (!apiJsFile.exists()) { + // 若文件存在则不重新生成 + apiJsFile.createNewFile(); + out = new FileOutputStream(apiJsFile); + apiJsTemplate.renderTo(out); + } + if (out != null) { + out.close(); + } + return apiJsFileUrl; + } + + /** + * 代码生成:generateView + */ + private String generateView(Template viewTemplate, + GeneratorConfig generatorConfig, + List> ruleList) throws IOException { + OutputStream out = null; + + //生成mapper代码 + viewTemplate.binding("generatorConfig", generatorConfig); + viewTemplate.binding("ruleList", ruleList); + String viewResult = viewTemplate.render(); + + //创建文件 + String viewFileUrl = generatorConfig.getFrontProjectDir() + "/src/view/" + generatorConfig.getViewParentDir() + "/" + generatorConfig.getResourceName() + ".vue"; + File viewFile = new File(viewFileUrl); + File viewDir = viewFile.getParentFile(); + if (!viewDir.exists()) { + viewDir.mkdirs(); + } + if (viewFile.exists()) { + viewFile.delete(); + } + if (!viewFile.exists()) { + // 若文件存在则不重新生成 + viewFile.createNewFile(); + out = new FileOutputStream(viewFile); + viewTemplate.renderTo(out); + } + if (out != null) { + out.close(); + } + return viewFileUrl; + } + + /** + * 代码生成:generateMapperXml + */ + private String generateMapperXml(Template mapperXmlTemplate, + GeneratorConfig generatorConfig, + List> ruleList) throws IOException { + OutputStream out = null; + + //生成mapper代码 + mapperXmlTemplate.binding("generatorConfig", generatorConfig); + mapperXmlTemplate.binding("ruleList", ruleList); + String mapperXmlResult = mapperXmlTemplate.render(); + + //创建文件 + String mapperXmlFileUrl = generatorConfig.getAfterProjectDir() + "/src/main/resources/mapper/" + generatorConfig.getClassName() + "Mapper.xml"; + File mapperXmlFile = new File(mapperXmlFileUrl); + File mapperXmlDir = mapperXmlFile.getParentFile(); + if (!mapperXmlDir.exists()) { + mapperXmlDir.mkdirs(); + } + if (mapperXmlFile.exists()) { + mapperXmlFile.delete(); + } + if (!mapperXmlFile.exists()) { + // 若文件存在则不重新生成 + mapperXmlFile.createNewFile(); + out = new FileOutputStream(mapperXmlFile); + mapperXmlTemplate.renderTo(out); + } + if (out != null) { + out.close(); + } + return mapperXmlFileUrl; + } + + /** + * 代码生成:generateMapper + */ + private String generateMapper(Template mapperTemplate, GeneratorConfig generatorConfig) throws IOException { + OutputStream out = null; + + //生成mapper代码 + mapperTemplate.binding("generatorConfig", generatorConfig); + String mapperResult = mapperTemplate.render(); + + //创建文件 + String mapperFileUrl = generatorConfig.getAfterProjectDir() + "/src/main/java/" + dotToLine(generatorConfig.getMapperPackage()) + "/" + generatorConfig.getClassName() + "Mapper.java"; + File mapperFile = new File(mapperFileUrl); + File mapperDir = mapperFile.getParentFile(); + if (!mapperDir.exists()) { + mapperDir.mkdirs(); + } + if (mapperFile.exists()) { + mapperFile.delete(); + } + if (!mapperFile.exists()) { + // 若文件存在则不重新生成 + mapperFile.createNewFile(); + out = new FileOutputStream(mapperFile); + mapperTemplate.renderTo(out); + } + if (out != null) { + out.close(); + } + + return mapperFileUrl; + } + + /** + * 代码生成:generateEntity + */ + private String generateEntity(Template entityTemplate, + GeneratorConfig generatorConfig, + List> ruleList) throws IOException { + OutputStream out = null; + + //生成generatorConfig代码 + entityTemplate.binding("generatorConfig", generatorConfig); + entityTemplate.binding("ruleList", ruleList); + String generatorConfigResult = entityTemplate.render(); + + //创建文件 + String entityFileUrl = generatorConfig.getAfterProjectDir() + "/src/main/java/" + dotToLine(generatorConfig.getEntityPackage()) + "/" + generatorConfig.getClassName() + ".java"; + File entityFile = new File(entityFileUrl); + File entityDir = entityFile.getParentFile(); + if (!entityDir.exists()) { + entityDir.mkdirs(); + } + if (entityFile.exists()) { + entityFile.delete(); + } + if (!entityFile.exists()) { + // 若文件存在则不重新生成 + entityFile.createNewFile(); + out = new FileOutputStream(entityFile); + entityTemplate.renderTo(out); + } + if (out != null) { + out.close(); + } + + return entityFileUrl; + } + + /** + * 代码生成:generateService + */ + private String generateService(Template serviceTemplate, GeneratorConfig generatorConfig) throws IOException { + OutputStream out = null; + + //生成service代码 + serviceTemplate.binding("generatorConfig", generatorConfig); + String serviceResult = serviceTemplate.render(); + + //创建文件 + String serviceFileUrl = generatorConfig.getAfterProjectDir() + "/src/main/java/" + dotToLine(generatorConfig.getServicePackage()) + "/" + generatorConfig.getClassName() + "Service.java"; + File serviceFile = new File(serviceFileUrl); + File serviceDir = serviceFile.getParentFile(); + if (!serviceDir.exists()) { + serviceDir.mkdirs(); + } + if (serviceFile.exists()) { + serviceFile.delete(); + } + if (!serviceFile.exists()) { + // 若文件存在则不重新生成 + serviceFile.createNewFile(); + out = new FileOutputStream(serviceFile); + serviceTemplate.renderTo(out); + } + if (out != null) { + out.close(); + } + + return serviceFileUrl; + } + + /** + * 代码生成:generateServiceImpl + */ + private String generateServiceImpl(Template serviceImplTemplate, GeneratorConfig generatorConfig) throws IOException { + OutputStream out = null; + + //生成serviceImpl代码 + serviceImplTemplate.binding("generatorConfig", generatorConfig); + String serviceImplResult = serviceImplTemplate.render(); + + //创建文件 + String serviceImplFileUrl = generatorConfig.getAfterProjectDir() + "/src/main/java/" + dotToLine(generatorConfig.getServiceImplPackage()) + "/" + generatorConfig.getClassName() + "ServiceImpl.java"; + File serviceImplFile = new File(serviceImplFileUrl); + File serviceImplDir = serviceImplFile.getParentFile(); + if (!serviceImplDir.exists()) { + serviceImplDir.mkdirs(); + } + if (serviceImplFile.exists()) { + serviceImplFile.delete(); + } + if (!serviceImplFile.exists()) { + // 若文件存在则不重新生成 + serviceImplFile.createNewFile(); + out = new FileOutputStream(serviceImplFile); + serviceImplTemplate.renderTo(out); + } + + if (out != null) { + out.close(); + } + + return serviceImplFileUrl; + } + + /** + * 代码生成:generateController + */ + private String generateController(Template controllerTemplate, GeneratorConfig generatorConfig) throws IOException { + OutputStream out = null; + + //生成controller代码 + controllerTemplate.binding("generatorConfig", generatorConfig); + String controllerResult = controllerTemplate.render(); + + //创建文件 + String controllerFileUrl = generatorConfig.getAfterProjectDir() + "/src/main/java/" + dotToLine(generatorConfig.getControllerPackage()) + "/" + generatorConfig.getClassName() + "Controller.java"; + File controllerFile = new File(controllerFileUrl); + File controllerDir = controllerFile.getParentFile(); + if (!controllerDir.exists()) { + controllerDir.mkdirs(); + } + if (controllerFile.exists()) { + controllerFile.delete(); + } + if (!controllerFile.exists()) { + // 若文件存在则不重新生成 + controllerFile.createNewFile(); + out = new FileOutputStream(controllerFile); + controllerTemplate.renderTo(out); + } + if (out != null) { + out.close(); + } + + return controllerFileUrl; + } + + /** + * 工具方法:点转斜线 + * + * @param str + */ + private String dotToLine(String str) { + return str.replace(".", "/"); + } + + /** + * 工具方法:下划线_转连字符- + * + * @param str + */ + private String underlineToHyphen(String str) { + return str.replace("_", "-"); + } + + /** + * 下划线转驼峰法 + * @param line 源字符串 + * @param smallCamel 大小驼峰,是否为小驼峰 + * @return 转换后的字符串 + */ + private String underlineToCamel(String line,boolean smallCamel){ + if(line==null||"".equals(line)){ + return ""; + } + StringBuffer sb = new StringBuffer(); + Pattern pattern = Pattern.compile("([A-Za-z\\d]+)(_)?"); + Matcher matcher = pattern.matcher(line); + while(matcher.find()){ + String word = matcher.group(); + sb.append(smallCamel&&matcher.start()==0?Character.toLowerCase(word.charAt(0)):Character.toUpperCase(word.charAt(0))); + int index=word.lastIndexOf('_'); + if(index>0){ + sb.append(word.substring(1, index).toLowerCase()); + }else{ + sb.append(word.substring(1).toLowerCase()); + } + } + return sb.toString(); + } + + /** + * 工具方法:驼峰法转下划线 + * + * @param str + */ + private String cameltoUnderline(String str) { + + if (StringUtils.isBlank(str)) { + return ""; + } + if (str.length() == 1) { + return str.toLowerCase(); + } + StringBuffer sb = new StringBuffer(); + for (int i = 1; i < str.length(); i++) { + if (Character.isUpperCase(str.charAt(i))) { + sb.append("_" + Character.toLowerCase(str.charAt(i))); + } else { + sb.append(str.charAt(i)); + } + } + return (str.charAt(0) + sb.toString()).toLowerCase(); + } + + /** + * 工具方法:首字母小写 + * + * @param str + */ + private String first2LowerCase(String str) { + + if (StringUtils.isBlank(str)) { + return ""; + } + if (str.length() == 1) { + return str.toLowerCase(); + } + StringBuffer sb = new StringBuffer(); + sb.append(Character.toLowerCase(str.charAt(0))); + sb.append(str.substring(1, str.length())); + return sb.toString(); + } + +} diff --git a/src/main/java/com/freedom/admin/controller/TSysDeptController.java b/src/main/java/com/freedom/admin/controller/TSysDeptController.java new file mode 100644 index 0000000..99cd8ba --- /dev/null +++ b/src/main/java/com/freedom/admin/controller/TSysDeptController.java @@ -0,0 +1,111 @@ +package com.freedom.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.freedom.admin.common.Result; +import com.freedom.admin.entity.TSysDept; +import com.freedom.admin.service.TSysDeptService; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.annotations.Param; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.security.PermitAll; +import java.util.List; +import java.util.Map; + +/** + * @author zhangjiaxun + * @date 2021/2/8 15:39 + * @description 系统-部门表 + */ +@RestController +@RequestMapping("/sys-dept") +@Log4j2 +public class TSysDeptController { + + @Autowired + private TSysDeptService tSysDeptService; + + /** + * 列表查询 + * + * @return Result + */ + @RequestMapping("/findPageList") + @PermitAll + public Result findPageList(@Param("pageNum") long pageNum, + @Param("pageSize") long pageSize, + TSysDept tSysDept){ + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("del_flag",0); + queryWrapper.eq(tSysDept.getParentId() != null,"parent_id",tSysDept.getParentId()); + queryWrapper.like(StringUtils.isNotBlank(tSysDept.getDeptName()),"dept_name", tSysDept.getDeptName()); + Page page = tSysDeptService.page(new Page<>(pageNum,pageSize), queryWrapper); + return Result.ok().setData(page); + } + + + /** + * 获取部门Tree数据 + * + * @return Result + */ + @RequestMapping("/getTreeList") + @PermitAll + public Result getTreeList() { + List> treeList = tSysDeptService.getTreeList(); + return Result.ok().setData(treeList); + } + + /** + * 获取部门TreeSelect数据 + * + * @return Result + */ + @RequestMapping("/getTreeSelectList") + @PermitAll + public Result getTreeSelectList() { + List> treeSelectListList = tSysDeptService.getTreeSelectList(); + return Result.ok().setData(treeSelectListList); + } + + /** + * 添加 + * + * @param tSysDept + * @return Result + */ + @RequestMapping("/add") + @PermitAll + public Result add(TSysDept tSysDept) { + boolean save = tSysDeptService.save(tSysDept); + return Result.ok().setData(save); + } + + /** + * 更新 + * + * @param tSysDept + * @return Result + */ + @RequestMapping("/update") + @PermitAll + public Result update(TSysDept tSysDept) { + log.info("先查询出父级部门,将要更新的子级部门level+1"); + Long parentId = 0L; + if (parentId.equals(tSysDept.getParentId())){ + tSysDept.setLevel(1); + }else { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("id",tSysDept.getParentId()); + TSysDept parentDept = tSysDeptService.getOne(queryWrapper); + tSysDept.setLevel(parentDept.getLevel() + 1); + } + boolean update = tSysDeptService.updateById(tSysDept); + return Result.ok().setData(update); + } + +} diff --git a/src/main/java/com/freedom/admin/controller/TSysPermissionController.java b/src/main/java/com/freedom/admin/controller/TSysPermissionController.java new file mode 100644 index 0000000..b2a3e4a --- /dev/null +++ b/src/main/java/com/freedom/admin/controller/TSysPermissionController.java @@ -0,0 +1,157 @@ +package com.freedom.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.freedom.admin.base.BaseController; +import com.freedom.admin.common.Result; +import com.freedom.admin.entity.TSysDept; +import com.freedom.admin.entity.TSysPermission; +import com.freedom.admin.service.TSysPermissionService; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.annotations.Param; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.security.PermitAll; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * @author zhangjiaxun + * @date 2021/2/4 11:00 + * @description 控制层-系统权限 + */ +@RestController +@RequestMapping("/sys-permission") +@Log4j2 +public class TSysPermissionController extends BaseController { + + @Autowired + TSysPermissionService tSysPermissionService; + + /** + * 登录成功时,获取登录用户权限数据 + * + * @return Result + * */ + @RequestMapping("/getPermissionList") + @PreAuthorize("permitAll()") + private Result> getPermissionList(){ + List> permissionList = tSysPermissionService.getPermissionListByUserId(getUser().getId()); + return Result.ok().setData(permissionList); + } + + /** + * 获取权限tree数据 + * + * @return Result + */ + @RequestMapping("/getTreeList") + @PreAuthorize("permitAll()") + private Result> getTreeList(){ + List> treeList = tSysPermissionService.getTreeList(); + return Result.ok().setData(treeList); + } + + /** + * 获取权限TreeSelect数据 + * + * @return Result + */ + @RequestMapping("/getTreeSelectList") + @PermitAll + public Result getTreeSelectList() { + List> treeSelectListList = tSysPermissionService.getTreeSelectList(); + return Result.ok().setData(treeSelectListList); + } + + /** + * 菜单权限页面-获取菜单列表数据 + * + * @return Result + */ + @RequestMapping("/findPageList") + @PreAuthorize("permitAll()") + private Result findPageList(@Param("pageNum") long pageNum, + @Param("pageSize") long pageSize, + TSysPermission tSysPermission){ + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("del_flag",0); + queryWrapper.eq(tSysPermission.getParentId() != null,"parent_id", tSysPermission.getParentId()); + queryWrapper.like(StringUtils.isNotBlank(tSysPermission.getPermissionName()),"permission_name", tSysPermission.getPermissionName()); + Page page = tSysPermissionService.page(new Page<>(pageNum,pageSize), queryWrapper); + return Result.ok().setData(page); + } + + /** + * 菜单权限页面-获取菜单树 + * + * @return Result + */ + @RequestMapping("/getTreeListByRoleId") + @PreAuthorize("permitAll()") + private Result getTreeListByRoleId(@Param("roleId") Long roleId){ + List> treeList = tSysPermissionService.getTreeListByRoleId(roleId); + log.info("treeList:{}",treeList); + return Result.ok().setData(treeList); + } + + /** + * 添加 + * + * @param tSysPermission + * @return Result + */ + @RequestMapping("/add") + @PermitAll + public Result add(TSysPermission tSysPermission) { + boolean save = tSysPermissionService.save(tSysPermission); + return Result.ok().setData(save); + } + + /** + * 更新 + * + * @param tSysPermission + * @return Result + */ + @RequestMapping("/update") + @PermitAll + public Result update(TSysPermission tSysPermission) { + log.info("先查询出父级权限,将要更新的子级部门level+1"); + Long parentId = 0L; + if (parentId.equals(tSysPermission.getParentId())){ + tSysPermission.setLevel(1); + }else { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("id",tSysPermission.getParentId()); + TSysPermission parentDept = tSysPermissionService.getOne(queryWrapper); + tSysPermission.setLevel(parentDept.getLevel() + 1); + } + boolean update = tSysPermissionService.updateById(tSysPermission); + return Result.ok().setData(update); + } + + /** + * 删除页面 + * + * @param tSysPermission + * @return Result + */ + @RequestMapping("/remove") + @PermitAll + public Result remove(TSysPermission tSysPermission) { + log.info("先查询出父级权限,将要更新的子级部门level+1"); + try { + boolean remove = tSysPermissionService.removeByPermissionId(tSysPermission.getId()); + return Result.ok().setData(remove); + } catch (Exception e) { + return Result.error(); + } + } + +} diff --git a/src/main/java/com/freedom/admin/controller/TSysRoleController.java b/src/main/java/com/freedom/admin/controller/TSysRoleController.java new file mode 100644 index 0000000..d53b1b9 --- /dev/null +++ b/src/main/java/com/freedom/admin/controller/TSysRoleController.java @@ -0,0 +1,105 @@ +package com.freedom.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.freedom.admin.base.BaseController; +import com.freedom.admin.common.Result; +import com.freedom.admin.config.MyException; +import com.freedom.admin.entity.TSysRole; +import com.freedom.admin.entity.TSysUser; +import com.freedom.admin.service.TSysRolePermissionService; +import com.freedom.admin.service.TSysRoleService; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.annotations.Param; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.security.PermitAll; +import java.util.List; +import java.util.Map; + +/** + * @author zhangjiaxun + * @date 2021/2/8 15:39 + * @description + */ +@RestController +@RequestMapping("/sys-role") +public class TSysRoleController extends BaseController { + + @Autowired + private TSysRoleService tSysRoleService; + + @Autowired + private TSysRolePermissionService tSysRolePermissionService; + + /** + * 获取角色数据列表 + * + * @return Result + * */ + @RequestMapping("/findPageList") + @PermitAll + public Result findPageList(@Param("pageNum") long pageNum, + @Param("pageSize") long pageSize, + TSysRole tSysRole){ + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("del_flag",0); + queryWrapper.eq(StringUtils.isNotBlank(tSysRole.getRoleName()),"role_name", tSysRole.getRoleName()); + Page page = tSysRoleService.page(new Page<>(pageNum,pageSize), queryWrapper); + return Result.ok().setData(page); + } + + /** + * 添加 + * + * @param tSysRole + * @return Result + */ + @RequestMapping("/add") + @PermitAll + public Result add(TSysRole tSysRole) { + boolean save = tSysRoleService.save(tSysRole); + return Result.ok().setData(save); + } + + /** + * 更新 + * + * @param tSysRole + * @return Result + */ + @RequestMapping("/update") + @PermitAll + public Result update(TSysRole tSysRole) { + boolean update = tSysRoleService.updateById(tSysRole); + return Result.ok().setData(update); + } + + /** + * 获取角色下拉列表数据 + * + * @return Result + */ + @RequestMapping("/getRoleSelectList") + public Result> getRoleSelectList() { + List> roleSelectList = tSysRoleService.getRoleSelectList(); + return Result.ok().setData(roleSelectList); + } + + /** + * 添加角色权限 + * */ + @RequestMapping("/addRolePermission") + @PermitAll + public Result addRolePermission(Long roleId, String rolePermissionStr){ + try { + boolean save = tSysRolePermissionService.save(roleId, rolePermissionStr, getUsername()); + return Result.ok().setData(save); + } catch (MyException e) { + return Result.error().code(e.getCode()).message(e.getMessage()); + } + } + +} diff --git a/src/main/java/com/freedom/admin/controller/TSysUserController.java b/src/main/java/com/freedom/admin/controller/TSysUserController.java new file mode 100644 index 0000000..775e123 --- /dev/null +++ b/src/main/java/com/freedom/admin/controller/TSysUserController.java @@ -0,0 +1,130 @@ +package com.freedom.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.freedom.admin.base.BaseController; +import com.freedom.admin.common.Result; +import com.freedom.admin.config.MyException; +import com.freedom.admin.entity.TSysUser; +import com.freedom.admin.service.TSysUserService; +import com.freedom.admin.service.UserService; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.annotations.Param; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.security.PermitAll; + +/** + * @author zhangjiaxun + * @date 2021/2/2 13:41 + * @description 控制层-用户表 + */ +@RestController +@RequestMapping("sys-user") +@Log4j2 +public class TSysUserController extends BaseController { + + @Autowired + private TSysUserService tSysUserService; + + @Autowired + private UserService userService; + + /** + * 用户注册 + * + * @param sysUser 用户信息 + * @return Result 响应结果 + * */ + @RequestMapping("register") + @PermitAll + public Result register(TSysUser sysUser){ + try { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("del_flag",0); + queryWrapper.eq("username", sysUser.getUsername()); + long count = tSysUserService.count(queryWrapper); + if (count > 0){ + return Result.error().message("账号已存在!"); + } + boolean register = tSysUserService.register(sysUser); + return Result.ok().setData(register); + } catch (MyException e) { + return Result.error().code(e.getCode()).message(e.getMessage()); + } + } + + /** + * 获取用户信息 + * */ + @RequestMapping("getUserInfo") + @PreAuthorize("hasAnyAuthority('user:getUserInfo')") + public Result getUserInfo(){ + TSysUser user = getUser(); + return Result.ok().setData(user); + } + + /** + * 获取用户数据列表 + * */ + @RequestMapping("findPageList") + @PermitAll + public Result findPageList(@Param("pageNum") long pageNum, + @Param("pageSize") long pageSize, + TSysUser tSysUser){ + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("del_flag",0); + queryWrapper.eq(StringUtils.isNotBlank(tSysUser.getUsername()),"username", tSysUser.getUsername()); + Page page = tSysUserService.page(new Page<>(pageNum,pageSize), queryWrapper); + return Result.ok().setData(page); + } + + /** + * 获取用户列表数据 + * */ + @RequestMapping("add") + @PermitAll + public Result add(TSysUser tSysUser,String userRoleStr){ + try { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("del_flag",0); + queryWrapper.eq("username", tSysUser.getUsername()); + long count = tSysUserService.count(queryWrapper); + if (count > 0){ + return Result.error().message("账号已存在!"); + } + boolean save = tSysUserService.save(tSysUser, userRoleStr, getUsername()); + return Result.ok().setData(save); + } catch (MyException e) { + return Result.error().code(e.getCode()).message(e.getMessage()); + } + + } + + @RequestMapping("update") + @PermitAll + public Result update(TSysUser tSysUser){ + try { + tSysUserService.updateById(tSysUser); + } catch (Exception e) { + return Result.error(); + } + return Result.ok(); + } + + @RequestMapping("remove") + @PermitAll + public Result remove(TSysUser tSysUser){ + try { + tSysUserService.removeById(tSysUser.getId()); + } catch (Exception e) { + return Result.error(); + } + return Result.ok(); + } + +} diff --git a/src/main/java/com/freedom/admin/entity/GeneratorConfig.java b/src/main/java/com/freedom/admin/entity/GeneratorConfig.java new file mode 100644 index 0000000..b38acf2 --- /dev/null +++ b/src/main/java/com/freedom/admin/entity/GeneratorConfig.java @@ -0,0 +1,103 @@ +package com.freedom.admin.entity; + +import lombok.Data; + +/** + * @author zhangjiaxun + * @date 2020/06/19 14:00 + * @description 生成代码的配置 + */ +@Data +public class GeneratorConfig { + + /** + * 前端项目所在系统路径 + * */ + private String frontProjectDir; + + /** + * 后端项目所在系统路径 + * */ + private String afterProjectDir; + + /** + * mapper文件生成路径 + * */ + private String mapperPackage; + + /** + * entity文件生成路径 + * */ + private String entityPackage; + + /** + * service文件生成路径 + * */ + private String servicePackage; + + /** + * serviceImpl文件生成路径 + * */ + private String serviceImplPackage; + + /** + * controller文件生成路径 + * */ + private String controllerPackage; + + /** + * 通用mapper文件路径 + * */ + private String basePackage; + + /** + * config文件路径 + * */ + private String configPackage; + + /** + * author作者名称 + * */ + private String author; + + /** + * date日期 + * */ + private String date; + + /** + * viewParentDir描述 + * */ + private String description; + + /** + * 类名 + * */ + private String className; + + /** + * 类名-小驼峰 + * */ + private String classNameLowerCase; + + /** + * 表名 + * */ + private String tableName; + + /** + * 资源名 + * */ + private String resourceName; + + /** + * 视图父目录 + * */ + private String viewParentDir; + + /** + * 使用模板 + * */ + private String usingTemplate; + +} diff --git a/src/main/java/com/freedom/admin/entity/GeneratorTableVO.java b/src/main/java/com/freedom/admin/entity/GeneratorTableVO.java new file mode 100644 index 0000000..3359827 --- /dev/null +++ b/src/main/java/com/freedom/admin/entity/GeneratorTableVO.java @@ -0,0 +1,58 @@ +package com.freedom.admin.entity; + +import lombok.Data; + +/** + * @description: 生成数据表的信息-值对象 + * @author: zhangjiaxun + * @date: 2021年03月20日 20:23:00 + */ +@Data +public class GeneratorTableVO { + + /** + * 表名 + */ + private String tableName; + + /** + * 字段名 + */ + private String columnName; + + /** + * 字段类型 + */ + private String columnType; + + /** + * 数据类型 + */ + private String dataType; + + /** + * 字段长度 + */ + private Double characterMaximumLength; + + /** + * 是否主键 + */ + private String isColumnKey; + + /** + * 是否为空 + */ + private String isNullable; + + /** + * 字段默认值 + */ + private String columnDefault; + + /** + * 注释 + */ + private String columnComment; + +} diff --git a/src/main/java/com/freedom/admin/entity/TSysDept.java b/src/main/java/com/freedom/admin/entity/TSysDept.java new file mode 100644 index 0000000..ae77c7e --- /dev/null +++ b/src/main/java/com/freedom/admin/entity/TSysDept.java @@ -0,0 +1,76 @@ +package com.freedom.admin.entity; + +import java.io.Serializable; +import java.util.Date; + +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; + +/** + * t_sys_dept + * @author + */ +@Data +public class TSysDept implements Serializable { + /** + * 主键 + */ + private Long id; + + /** + * 父ID + */ + private Long parentId; + + /** + * 编码 + */ + private String deptCode; + + /** + * 名称 + */ + private String deptName; + + /** + * 描述 + */ + private String deptDesc; + + /** + * 级别 + */ + private Integer level; + + /** + * 排序 + */ + private Integer sort; + + /** + * 创建者 + */ + private String createBy; + + /** + * 创建时间 + */ + private Date createDate; + + /** + * 更新者 + */ + private String updateBy; + + /** + * 更新时间 + */ + private Date updateDate; + + /** + * 删除标记 + */ + private Boolean delFlag; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/freedom/admin/entity/TSysPermission.java b/src/main/java/com/freedom/admin/entity/TSysPermission.java new file mode 100644 index 0000000..4cd9b33 --- /dev/null +++ b/src/main/java/com/freedom/admin/entity/TSysPermission.java @@ -0,0 +1,89 @@ +package com.freedom.admin.entity; + +import java.io.Serializable; +import java.util.Date; +import lombok.Data; + +/** + * t_sys_permission + * @author + */ +@Data +public class TSysPermission implements Serializable { + /** + * 主键 + */ + private Long id; + + /** + * 父ID + */ + private Long parentId; + + /** + * 编码 + */ + private String permissionCode; + + /** + * 名称 + */ + private String permissionName; + + /** + * 描述 + */ + private String permissionDesc; + + /** + * 级别 + */ + private Integer level; + + /** + * 图标 + */ + private String icon; + + /** + * 链接地址 + */ + private String url; + + /** + * 组件地址 + */ + private String component; + + /** + * 排序 + */ + private Integer sort; + + /** + * 创建者 + */ + private String createBy; + + /** + * 创建时间 + */ + private Date createDate; + + /** + * 更新者 + */ + private String updateBy; + + /** + * 编辑时间 + */ + private Date updateDate; + + /** + * 删除标记 + */ + private Boolean delFlag; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/freedom/admin/entity/TSysRole.java b/src/main/java/com/freedom/admin/entity/TSysRole.java new file mode 100644 index 0000000..b02acfd --- /dev/null +++ b/src/main/java/com/freedom/admin/entity/TSysRole.java @@ -0,0 +1,74 @@ +package com.freedom.admin.entity; + +import java.io.Serializable; +import java.util.Date; +import lombok.Data; + +/** + * t_sys_role + * @author + */ +@Data +public class TSysRole implements Serializable { + /** + * 主键 + */ + private Long id; + + /** + * 角色名称 + */ + private String roleName; + + /** + * 角色编号 + */ + private String roleCode; + + /** + * 角色描述 + */ + private String roleDesc; + + /** + * 默认角色 + */ + private String isDefault; + + /** + * 该角色归属部门 + */ + private Long deptId; + + /** + * 角色排序 + */ + private Integer sort; + + /** + * 创建者 + */ + private String createBy; + + /** + * 创建时间 + */ + private Date createDate; + + /** + * 更新者 + */ + private String updateBy; + + /** + * 更新时间 + */ + private Date updateDate; + + /** + * 删除标记 + */ + private Boolean delFlag; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/freedom/admin/entity/TSysRolePermission.java b/src/main/java/com/freedom/admin/entity/TSysRolePermission.java new file mode 100644 index 0000000..5b16f3f --- /dev/null +++ b/src/main/java/com/freedom/admin/entity/TSysRolePermission.java @@ -0,0 +1,39 @@ +package com.freedom.admin.entity; + +import java.io.Serializable; +import java.util.Date; +import lombok.Data; + +/** + * t_sys_role_permission + * @author + */ +@Data +public class TSysRolePermission implements Serializable { + /** + * 主键 + */ + private Long id; + + /** + * 角色ID + */ + private Long roleId; + + /** + * 权限ID + */ + private Long permissionId; + + /** + * 创建者 + */ + private String createBy; + + /** + * 创建时间 + */ + private Date createDate; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/freedom/admin/entity/TSysUser.java b/src/main/java/com/freedom/admin/entity/TSysUser.java new file mode 100644 index 0000000..d8dc8da --- /dev/null +++ b/src/main/java/com/freedom/admin/entity/TSysUser.java @@ -0,0 +1,94 @@ +package com.freedom.admin.entity; + +import java.io.Serializable; +import java.util.Date; +import lombok.Data; + +/** + * t_sys_user + * @author + */ +@Data +public class TSysUser implements Serializable { + /** + * 主键 + */ + private Long id; + + /** + * 登录用户名 + */ + private String username; + + /** + * 登陆密码 + */ + private String password; + + /** + * 加密方式(默认MD5) + */ + private String encodeType; + + /** + * 用户类型 + */ + private String userType; + + /** + * 用户昵称 + */ + private String nickname; + + /** + * 用户头像 + */ + private String avatar; + + /** + * 手机号 + */ + private String phone; + + /** + * 电子邮箱 + */ + private String email; + + /** + * 个性签名 + */ + private String signature; + + /** + * 状态 + */ + private Integer status; + + /** + * 创建者 + */ + private String createBy; + + /** + * 创建时间 + */ + private Date createDate; + + /** + * 更新者 + */ + private String updateBy; + + /** + * 更新时间 + */ + private Date updateDate; + + /** + * 删除标记 + */ + private Boolean delFlag; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/freedom/admin/entity/TSysUserRole.java b/src/main/java/com/freedom/admin/entity/TSysUserRole.java new file mode 100644 index 0000000..7773719 --- /dev/null +++ b/src/main/java/com/freedom/admin/entity/TSysUserRole.java @@ -0,0 +1,39 @@ +package com.freedom.admin.entity; + +import java.io.Serializable; +import java.util.Date; +import lombok.Data; + +/** + * t_sys_user_role + * @author + */ +@Data +public class TSysUserRole implements Serializable { + /** + * 主键 + */ + private Long id; + + /** + * 用户ID + */ + private Long userId; + + /** + * 角色ID + */ + private Long roleId; + + /** + * 创建者 + */ + private String createBy; + + /** + * 创建时间 + */ + private Date createTime; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/freedom/admin/mapper/GeneratorMapper.java b/src/main/java/com/freedom/admin/mapper/GeneratorMapper.java new file mode 100644 index 0000000..7777ba6 --- /dev/null +++ b/src/main/java/com/freedom/admin/mapper/GeneratorMapper.java @@ -0,0 +1,29 @@ +package com.freedom.admin.mapper; + +import com.freedom.admin.entity.GeneratorTableVO; + +import java.util.List; +import java.util.Map; + +/** + * @description: TODO + * @author: zhangjiaxun + * @date: 2021年03月20日 22:28:00 + */ +public interface GeneratorMapper { + + /** + * 分页查询mysql数据表 + * + * @return List> + * */ + List getTableListWithPage(GeneratorTableVO generatorTableVO); + + /** + * 获取mysql表数据下拉列表数据 + * + * @return List> + * */ + List> getTableSelectList(); + +} diff --git a/src/main/java/com/freedom/admin/mapper/TSysDeptMapper.java b/src/main/java/com/freedom/admin/mapper/TSysDeptMapper.java new file mode 100644 index 0000000..0cde75a --- /dev/null +++ b/src/main/java/com/freedom/admin/mapper/TSysDeptMapper.java @@ -0,0 +1,32 @@ +package com.freedom.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.freedom.admin.entity.TSysDept; + +import java.util.List; +import java.util.Map; + +/** + * @author zhangjiaxun + * @date 2021/2/2 09:30 + * @description 数据访问层-部门表 + */ +public interface TSysDeptMapper extends BaseMapper { + + /** + * 获取部门数据提供给部门树 + * + * @param + * @return List> + * */ + List> getDeptListForTree(); + + /** + * 获取部门数据提供给部门下拉树 + * + * @param + * @return List> + * */ + List> getDeptListForTreeSelect(); + +} \ No newline at end of file diff --git a/src/main/java/com/freedom/admin/mapper/TSysPermissionMapper.java b/src/main/java/com/freedom/admin/mapper/TSysPermissionMapper.java new file mode 100644 index 0000000..df67a07 --- /dev/null +++ b/src/main/java/com/freedom/admin/mapper/TSysPermissionMapper.java @@ -0,0 +1,61 @@ +package com.freedom.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.freedom.admin.entity.TSysPermission; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * @author zhangjiaxun + * @date 2021/2/2 09:30 + * @description 数据访问层-权限表 + */ +public interface TSysPermissionMapper extends BaseMapper { + + /** + * 查询权限数据 + * + * @param userId 用户ID + * @return List + * */ + List selectListByUserId(@Param("userId") Long userId); + + /** + * 查询权限数据 + * + * @param userId 用户ID + * @return List> + * */ + List> selectMapListByUserId(@Param("userId") Long userId); + + /** + * 查询所有权限数据 + * + * @return List> + * */ + List> selectAllMapList(); + + /** + * 查询所有权限数据,提供给树 + * + * @return List> + * */ + List> getPermissionListForTree(); + + /** + * 根据角色ID查询权限数据 + * + * @param roleId 角色ID + * @return List> + * */ + List> getPermissionListByRoleId(@Param("roleId") Long roleId); + + /** + * 获取权限数据提供给treeSelect + * + * @return List> + * */ + List> getPermissionListForTreeSelect(); +} \ No newline at end of file diff --git a/src/main/java/com/freedom/admin/mapper/TSysRoleMapper.java b/src/main/java/com/freedom/admin/mapper/TSysRoleMapper.java new file mode 100644 index 0000000..0aa3e7d --- /dev/null +++ b/src/main/java/com/freedom/admin/mapper/TSysRoleMapper.java @@ -0,0 +1,23 @@ +package com.freedom.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.freedom.admin.entity.TSysRole; + +import java.util.List; +import java.util.Map; + +/** + * @author zhangjiaxun + * @date 2021/2/2 09:30 + * @description 数据访问层-角色表 + */ +public interface TSysRoleMapper extends BaseMapper { + + /** + * 获取角色下拉列表数据 + * + * @return List> + * */ + List> getRoleSelectList(); + +} \ No newline at end of file diff --git a/src/main/java/com/freedom/admin/mapper/TSysRolePermissionMapper.java b/src/main/java/com/freedom/admin/mapper/TSysRolePermissionMapper.java new file mode 100644 index 0000000..3c7e2d9 --- /dev/null +++ b/src/main/java/com/freedom/admin/mapper/TSysRolePermissionMapper.java @@ -0,0 +1,13 @@ +package com.freedom.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.freedom.admin.entity.TSysRolePermission; + +/** + * @author zhangjiaxun + * @date 2021/2/2 09:30 + * @description 数据访问层-角色权限关系表 + */ +public interface TSysRolePermissionMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/src/main/java/com/freedom/admin/mapper/TSysUserMapper.java b/src/main/java/com/freedom/admin/mapper/TSysUserMapper.java new file mode 100644 index 0000000..d07cf41 --- /dev/null +++ b/src/main/java/com/freedom/admin/mapper/TSysUserMapper.java @@ -0,0 +1,16 @@ +package com.freedom.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.freedom.admin.entity.TSysUser; +import org.apache.ibatis.annotations.Param; + +/** + * @author zhangjiaxun + * @date 2021/2/2 09:30 + * @description 数据访问层-用户表 + */ +public interface TSysUserMapper extends BaseMapper { + + TSysUser selectOneByUsername(@Param("username") String username); + +} \ No newline at end of file diff --git a/src/main/java/com/freedom/admin/mapper/TSysUserRoleMapper.java b/src/main/java/com/freedom/admin/mapper/TSysUserRoleMapper.java new file mode 100644 index 0000000..614541d --- /dev/null +++ b/src/main/java/com/freedom/admin/mapper/TSysUserRoleMapper.java @@ -0,0 +1,13 @@ +package com.freedom.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.freedom.admin.entity.TSysUserRole; + +/** + * @author zhangjiaxun + * @date 2021/2/2 09:30 + * @description 数据访问层-用户角色关系表 + */ +public interface TSysUserRoleMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/src/main/java/com/freedom/admin/service/GeneratorService.java b/src/main/java/com/freedom/admin/service/GeneratorService.java new file mode 100644 index 0000000..86a3ce6 --- /dev/null +++ b/src/main/java/com/freedom/admin/service/GeneratorService.java @@ -0,0 +1,30 @@ +package com.freedom.admin.service; + +import com.freedom.admin.entity.GeneratorTableVO; +import com.github.pagehelper.PageInfo; + +import java.util.List; +import java.util.Map; + +/** + * @author: zhangjiaxun + * @date: 2021年03月20日 22:21:00 + * @description: 业务层接口-代码生成业务接口 + */ +public interface GeneratorService { + + /** + * 分页查询mysql数据表 + * + * @return List> + * */ + PageInfo getTableListWithPage(int pageNum, int pageSize, GeneratorTableVO generatorTableVO); + + /** + * 获取mysql表数据下拉列表数据 + * + * @return List> + * */ + List> getTableSelectList(); + +} diff --git a/src/main/java/com/freedom/admin/service/TSysDeptService.java b/src/main/java/com/freedom/admin/service/TSysDeptService.java new file mode 100644 index 0000000..9d58f00 --- /dev/null +++ b/src/main/java/com/freedom/admin/service/TSysDeptService.java @@ -0,0 +1,29 @@ +package com.freedom.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.freedom.admin.entity.TSysDept; + +import java.util.List; +import java.util.Map; + +/** + * @author zhangjiaxun + * @date 2021/2/9 10:15 + * @description + */ +public interface TSysDeptService extends IService { + + /** + * 获取部门Tree数据 + * @return List> + * */ + List> getTreeList(); + + /** + * 获取部门TreeSelect数据 + * + * @return List> + * */ + List> getTreeSelectList(); + +} diff --git a/src/main/java/com/freedom/admin/service/TSysPermissionService.java b/src/main/java/com/freedom/admin/service/TSysPermissionService.java new file mode 100644 index 0000000..5ea0772 --- /dev/null +++ b/src/main/java/com/freedom/admin/service/TSysPermissionService.java @@ -0,0 +1,52 @@ +package com.freedom.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.freedom.admin.entity.TSysPermission; + +import java.util.List; +import java.util.Map; + +/** + * @author zhangjiaxun + * @date 2021/2/4 11:08 + * @description + */ +public interface TSysPermissionService extends IService { + + /** + * 获取权限数据 + * + * @param userId 用户ID + * @return List> + * */ + List> getPermissionListByUserId(Long userId); + + /** + * 获取权限树数据 + * + * @return List> + * */ + List> getTreeList(); + + /** + * 获取权限树数据 + * + * @param roleId 角色ID + * @return List> + * */ + List> getTreeListByRoleId(Long roleId); + + /** + * 获取权限treeSelect数据 + * + * @return List> + * */ + List> getTreeSelectList(); + + /** + * 根据菜单ID进行递归删除 + * + * @return Boolean + * */ + Boolean removeByPermissionId(Long id); +} diff --git a/src/main/java/com/freedom/admin/service/TSysRolePermissionService.java b/src/main/java/com/freedom/admin/service/TSysRolePermissionService.java new file mode 100644 index 0000000..e5f1b78 --- /dev/null +++ b/src/main/java/com/freedom/admin/service/TSysRolePermissionService.java @@ -0,0 +1,15 @@ +package com.freedom.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.freedom.admin.entity.TSysRolePermission; + +/** + * @author zhangjiaxun + * @date 2021/2/14 9:21 + * @description + */ +public interface TSysRolePermissionService extends IService { + + boolean save(Long roleId, String rolePermissionStr, String operator); + +} diff --git a/src/main/java/com/freedom/admin/service/TSysRoleService.java b/src/main/java/com/freedom/admin/service/TSysRoleService.java new file mode 100644 index 0000000..cf63d50 --- /dev/null +++ b/src/main/java/com/freedom/admin/service/TSysRoleService.java @@ -0,0 +1,21 @@ +package com.freedom.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.freedom.admin.entity.TSysRole; + +import java.util.List; +import java.util.Map; + +/** + * @author zhangjiaxun + * @date 2021/2/8 15:45 + * @description + */ +public interface TSysRoleService extends IService { + + /** + * 获取角色下拉列表数据 + */ + List> getRoleSelectList(); + +} diff --git a/src/main/java/com/freedom/admin/service/TSysUserService.java b/src/main/java/com/freedom/admin/service/TSysUserService.java new file mode 100644 index 0000000..d5bda0c --- /dev/null +++ b/src/main/java/com/freedom/admin/service/TSysUserService.java @@ -0,0 +1,23 @@ +package com.freedom.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.freedom.admin.entity.TSysUser; + +/** + * @author zhangjiaxun + * @date 2021/2/4 9:28 + * @description + */ +public interface TSysUserService extends IService { + + /** + * 添加用户账号 + * */ + boolean save(TSysUser entity, String userRoleStr, String operator); + + /** + * 用户注册 + * */ + boolean register(TSysUser entity); + +} diff --git a/src/main/java/com/freedom/admin/service/UserService.java b/src/main/java/com/freedom/admin/service/UserService.java new file mode 100644 index 0000000..9064806 --- /dev/null +++ b/src/main/java/com/freedom/admin/service/UserService.java @@ -0,0 +1,12 @@ +package com.freedom.admin.service; + +import org.springframework.security.core.userdetails.UserDetailsService; + +/** + * @author zhangjiaxun + * @date 2021/1/5 18:58 + * @description + */ +public interface UserService extends UserDetailsService { + +} diff --git a/src/main/java/com/freedom/admin/service/impl/GeneratorServiceImpl.java b/src/main/java/com/freedom/admin/service/impl/GeneratorServiceImpl.java new file mode 100644 index 0000000..d68357d --- /dev/null +++ b/src/main/java/com/freedom/admin/service/impl/GeneratorServiceImpl.java @@ -0,0 +1,40 @@ +package com.freedom.admin.service.impl; + +import com.freedom.admin.entity.GeneratorTableVO; +import com.freedom.admin.mapper.GeneratorMapper; +import com.freedom.admin.service.GeneratorService; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * @author zhangjiaxun + * @date 2021/2/4 9:29 + * @description 业务层实现-代码生成业务 + */ +@Service +@Log4j2 +public class GeneratorServiceImpl implements GeneratorService { + + @Autowired + private GeneratorMapper generatorMapper; + + @Override + public PageInfo getTableListWithPage(int pageNum, int pageSize, GeneratorTableVO generatorTableVO) { + PageHelper.startPage(pageNum, pageSize); + List list = generatorMapper.getTableListWithPage(generatorTableVO); + PageInfo pageInfo = new PageInfo<>(list); + return pageInfo; + } + + @Override + public List> getTableSelectList() { + return generatorMapper.getTableSelectList(); + } + +} diff --git a/src/main/java/com/freedom/admin/service/impl/TSysDeptImpl.java b/src/main/java/com/freedom/admin/service/impl/TSysDeptImpl.java new file mode 100644 index 0000000..5a43caf --- /dev/null +++ b/src/main/java/com/freedom/admin/service/impl/TSysDeptImpl.java @@ -0,0 +1,120 @@ +package com.freedom.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.freedom.admin.entity.TSysDept; +import com.freedom.admin.mapper.TSysDeptMapper; +import com.freedom.admin.service.TSysDeptService; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author zhangjiaxun + * @date 2021/2/9 10:23 + * @description + */ +@Service +@Log4j2 +public class TSysDeptImpl extends ServiceImpl implements TSysDeptService { + + private static final int TYPE_FOR_TREE = 1; + private static final int TYPE_FOR_TREE_SELECT = 2; + + @Autowired + private TSysDeptMapper tSysDeptMapper; + + @Override + public List> getTreeList() { + log.info("业务层--获取部门数据,提供给树"); + List> deptList = tSysDeptMapper.getDeptListForTree(); + log.info("业务层--添加顶级元素"); + addTopDeptElement(deptList, TYPE_FOR_TREE); + log.info("业务层--格式化部门数据为树形结构"); + List> treeList = formatDeptListToTreeList(deptList, -1L); + return treeList; + } + + @Override + public List> getTreeSelectList() { + log.info("业务层--获取部门数据"); + List> deptList = tSysDeptMapper.getDeptListForTreeSelect(); + log.info("业务层--添加顶级元素"); + addTopDeptElement(deptList, TYPE_FOR_TREE_SELECT); + log.info("业务层--格式化部门数据"); + List> treeSelectList = formatDeptListToTreeSelectList(deptList, -1L); + return treeSelectList; + } + + /** + * deptList数据格式化树形结构方法 + */ + public List> formatDeptListToTreeList(List> deptList, Long parentId) { + List> resultList = new ArrayList<>(); + for (Map dept : deptList) { + if ("0".equals(dept.get("level").toString())){ + dept.put("selected",true); + dept.put("expand",true); + } + if (dept.get("checked") != null){ + if ("1".equals(dept.get("checked"))){ + dept.put("checked",true); + }else if ("0".equals(dept.get("checked"))){ + dept.put("checked",false); + } + } + if (parentId.equals(dept.get("parentId"))) { + List> childList = formatDeptListToTreeList(deptList, Long.valueOf(dept.get("id").toString())); + dept.put("children", childList); + resultList.add(dept); + } + } + return resultList; + } + + + /** + * 添加顶级元素 + * */ + public void addTopDeptElement(List> deptList, int type){ + Map topDeptElementMap = new HashMap<>(); + if (type == TYPE_FOR_TREE){ + topDeptElementMap.put("id", 0L); + topDeptElementMap.put("parentId", -1L); + topDeptElementMap.put("title", "顶级"); + topDeptElementMap.put("deptCode", "dept_top"); + topDeptElementMap.put("deptDesc","dept_desc"); + topDeptElementMap.put("level", 0); + topDeptElementMap.put("delFlag", 0); + }else if (type == TYPE_FOR_TREE_SELECT){ + topDeptElementMap.put("id", 0L); + topDeptElementMap.put("parentId", -1L); + topDeptElementMap.put("label", "顶级"); + topDeptElementMap.put("level", 0); + } + + deptList.add(topDeptElementMap); + } + + /** + * deptList数据格式化下拉树数据 + */ + private List> formatDeptListToTreeSelectList(List> deptList, Long parentId) { + List> resultList = new ArrayList<>(); + for (Map dept : deptList) { + if (parentId.equals(dept.get("parentId"))) { + List> childList = formatDeptListToTreeSelectList(deptList, Long.valueOf(dept.get("id").toString())); + if (childList.size()>0 ){ + dept.put("children", childList); + } + resultList.add(dept); + } + } + return resultList; + } + +} diff --git a/src/main/java/com/freedom/admin/service/impl/TSysPermissionServiceImpl.java b/src/main/java/com/freedom/admin/service/impl/TSysPermissionServiceImpl.java new file mode 100644 index 0000000..81c1083 --- /dev/null +++ b/src/main/java/com/freedom/admin/service/impl/TSysPermissionServiceImpl.java @@ -0,0 +1,231 @@ +package com.freedom.admin.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.freedom.admin.entity.TSysPermission; +import com.freedom.admin.mapper.TSysPermissionMapper; +import com.freedom.admin.service.TSysPermissionService; +import com.google.common.base.Throwables; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author zhangjiaxun + * @date 2021/2/4 11:08 + * @description 业务层-系统权限 + */ +@Service +@Log4j2 +public class TSysPermissionServiceImpl extends ServiceImpl implements TSysPermissionService { + + private static final int TYPE_FOR_TREE = 1; + private static final int TYPE_FOR_TREE_SELECT = 2; + + @Autowired + private TSysPermissionMapper tSysPermissionMapper; + + @Override + public List> getPermissionListByUserId(Long userId) { + log.info("业务层---查询所有权限"); + List> allPermissionList = tSysPermissionMapper.selectAllMapList(); + log.info("业务层--查询用户权限"); + List> userPermissionList = tSysPermissionMapper.selectMapListByUserId(userId); + log.info("业务层--拼接出完整的权限"); + List> permissionList = getPermissionList(allPermissionList, userPermissionList); + log.info("业务层--添加顶级元素"); + addTopPermissionElement(permissionList, TYPE_FOR_TREE); + log.info("业务层--格式化数据为树形结构--结果:{}",permissionList); + List> permissionTreeList = formatPermissionListToTreeList(permissionList, -1L); + if (permissionTreeList !=null && permissionTreeList.size()>0){ + List> childrenList = (List>)permissionTreeList.get(0).get("children"); + return childrenList; + } + return permissionList; + } + + @Override + public List> getTreeList() { + log.info("业务层---查询所有权限提供给树"); + List> permissionListForTree = tSysPermissionMapper.getPermissionListForTree(); + log.info("业务层--添加顶级元素"); + addTopPermissionElement(permissionListForTree, TYPE_FOR_TREE); + log.info("业务层---格式化树数据为树形结构"); + List> treeList = formatPermissionListToTreeList(permissionListForTree, -1L); + return treeList; + } + + @Override + public List> getTreeSelectList() { + log.info("业务层--获取部门数据"); + List> permissionList = tSysPermissionMapper.getPermissionListForTreeSelect(); + log.info("业务层--添加顶级元素"); + addTopPermissionElement(permissionList, TYPE_FOR_TREE_SELECT); + log.info("业务层--格式化权限数据"); + List> treeSelectList = formatPermissionListToTreeSelectList(permissionList, -1L); + return treeSelectList; + } + + @Override + public List> getTreeListByRoleId(Long roleId) { + log.info("业务层---查询所有权限提供给树"); + List> permissionListForTree = tSysPermissionMapper.getPermissionListByRoleId(roleId); + log.info("业务层--添加顶级元素"); + addTopPermissionElement(permissionListForTree, TYPE_FOR_TREE); + log.info("业务层---格式化树数据为树形结构"); + List> treeList = formatPermissionListToTreeList(permissionListForTree, -1L); + return treeList; + } + + /** + * 添加上级权限 + * */ + public List> getPermissionList(List> allPermissionList, + List> userPermissionList){ + HashSet> permissionSet = new HashSet<>(); + if (userPermissionList !=null && userPermissionList.size()>0){ + // 循环添加数据,实际我们的菜单分为多级 + for (Map userPermissionMap : userPermissionList) { + for (Map allPermissionMap : allPermissionList) { + if (allPermissionMap.get("parentId").toString().equals("-1")){ + // 保证至少会有一个顶级菜单 + if (!permissionSet.contains(allPermissionMap)){ + // 保证不重复加入菜单列表结果集合中 + permissionSet.add(allPermissionMap); + } + } + + if (userPermissionMap.get("id").toString().equals(allPermissionMap.get("id").toString())){ + // 把自己菜单加入结果集合中 + permissionSet.add(allPermissionMap); + } + + if (userPermissionMap.get("parentId").toString().equals(allPermissionMap.get("id").toString())){ + if (!permissionSet.contains(allPermissionMap)){ + // 保证不重复加入菜单列表结果集合中 + permissionSet.add(allPermissionMap); + } + } + + } + } + } + List> permissionList = new ArrayList<>(permissionSet); + return permissionList; + } + + /** + * permissionList数据格式化树形结构方法 + * + * @param permissionList 权限数据 + * @param parentId 父ID + * @return List> + */ + private List> formatPermissionListToTreeList(List> permissionList, Long parentId) { + List> resultList = new ArrayList<>(); + for (Map permission : permissionList) { + if ("0".equals(permission.get("level").toString())){ + //permission.put("selected", true); + permission.put("checked", false); + } + permission.put("expand", true); + if (permission.get("checked") != null){ + if ("1".equals(permission.get("checked"))){ + permission.put("checked", true); + }else if ("0".equals(permission.get("checked"))){ + permission.put("checked", false); + } + } + if (parentId.equals(permission.get("parentId"))) { + List> childList = formatPermissionListToTreeList(permissionList, Long.valueOf(permission.get("id").toString())); + List> sortList = childList.stream().sorted(Comparator.comparing(map -> Integer.parseInt(map.get("sort").toString()))).collect(Collectors.toList()); + permission.put("children", sortList); + resultList.add(permission); + } + } + return resultList; + } + + /** + * permissionList格式化treeSelect结构 + * + * @param permissionList 权限数据 + * @param parentId 父ID + * @return List> + */ + private List> formatPermissionListToTreeSelectList(List> permissionList, Long parentId) { + List> resultList = new ArrayList<>(); + for (Map permission : permissionList) { + if ("0".equals(permission.get("level"))){ + permission.put("isDisabled",true); + } + if (parentId.equals(permission.get("parentId"))) { + List> childList = formatPermissionListToTreeSelectList(permissionList, Long.valueOf(permission.get("id").toString())); + if (childList.size()>0 ){ + permission.put("children", childList); + } + resultList.add(permission); + } + } + return resultList; + } + + /** + * 添加顶级元素 + * */ + public void addTopPermissionElement(List> permissionList, int type){ + Map topPermissionElementMap = new HashMap<>(); + if (type == TYPE_FOR_TREE){ + topPermissionElementMap.put("id", 0L); + topPermissionElementMap.put("parentId", -1L); + topPermissionElementMap.put("title", "顶级"); + topPermissionElementMap.put("icon", "ios-navigate"); + topPermissionElementMap.put("level", 0); + topPermissionElementMap.put("checked", 1); + topPermissionElementMap.put("sort", 0); + }else if (type == TYPE_FOR_TREE_SELECT){ + topPermissionElementMap.put("id", 0L); + topPermissionElementMap.put("parentId", -1L); + topPermissionElementMap.put("label", "顶级"); + topPermissionElementMap.put("level", 0); + topPermissionElementMap.put("sort", 0); + } + + permissionList.add(topPermissionElementMap); + } + + /** + * 递归删除 + * + * @param id 父ID + * @return Boolean ture/false + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean removeByPermissionId(Long id) { + try { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("parent_id", id); + queryWrapper.eq("del_flag",0); + List childrenList = tSysPermissionMapper.selectList(queryWrapper); + // 子菜单集合为空时,删除当前菜单,跳出此循环4 + if (childrenList == null || childrenList.size() == 0){ + tSysPermissionMapper.deleteById(id); + return Boolean.TRUE; + } + for (TSysPermission tSysPermission : childrenList) { + removeByPermissionId(tSysPermission.getId()); + } + + } catch (Exception e) { + log.error("递归删除菜单权限id:{}异常:{}", id ,Throwables.getStackTraceAsString(e)); + return Boolean.FALSE; + } + return Boolean.TRUE; + } + +} diff --git a/src/main/java/com/freedom/admin/service/impl/TSysRolePermissionServiceImpl.java b/src/main/java/com/freedom/admin/service/impl/TSysRolePermissionServiceImpl.java new file mode 100644 index 0000000..8ecb98c --- /dev/null +++ b/src/main/java/com/freedom/admin/service/impl/TSysRolePermissionServiceImpl.java @@ -0,0 +1,55 @@ +package com.freedom.admin.service.impl; + +import com.alibaba.fastjson.JSONArray; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.freedom.admin.config.MyException; +import com.freedom.admin.entity.TSysRolePermission; +import com.freedom.admin.mapper.TSysRolePermissionMapper; +import com.freedom.admin.service.TSysRolePermissionService; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; + +/** + * @author zhangjiaxun + * @date 2021/2/14 9:23 + * @description + */ +@Service +@Log4j2 +public class TSysRolePermissionServiceImpl extends ServiceImpl implements TSysRolePermissionService{ + + @Autowired + private TSysRolePermissionMapper tSysRolePermissionMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean save(Long roleId, String rolePermissionStr, String operator) { + log.info("业务层-删除权限"); + QueryWrapper deleteWrapper = new QueryWrapper<>(); + deleteWrapper.eq("role_id",roleId); + int deleteRolePermission = tSysRolePermissionMapper.delete(deleteWrapper); + List tSysRolePermissionList = JSONArray.parseArray(rolePermissionStr, TSysRolePermission.class); + if (tSysRolePermissionList == null || tSysRolePermissionList.size() == 0){ + throw new MyException(400,"添加用户权限为空!"); + } + log.info("业务层-添加权限"); + int insertRolePermissionCount = 0; + for (TSysRolePermission tSysRolePermission : tSysRolePermissionList) { + tSysRolePermission.setCreateBy(operator); + tSysRolePermission.setCreateDate(new Date()); + int insertTSysUserRole = tSysRolePermissionMapper.insert(tSysRolePermission); + insertRolePermissionCount = insertRolePermissionCount + insertTSysUserRole; + } + if (insertRolePermissionCount != tSysRolePermissionList.size()){ + throw new MyException(400,"添加权限失败!"); + } + return true; + } + +} diff --git a/src/main/java/com/freedom/admin/service/impl/TSysRoleServiceImpl.java b/src/main/java/com/freedom/admin/service/impl/TSysRoleServiceImpl.java new file mode 100644 index 0000000..2d4cf1b --- /dev/null +++ b/src/main/java/com/freedom/admin/service/impl/TSysRoleServiceImpl.java @@ -0,0 +1,28 @@ +package com.freedom.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.freedom.admin.entity.TSysRole; +import com.freedom.admin.mapper.TSysRoleMapper; +import com.freedom.admin.service.TSysRoleService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * @author zhangjiaxun + * @date 2021/2/8 15:47 + * @description + */ +@Service +public class TSysRoleServiceImpl extends ServiceImpl implements TSysRoleService { + + @Autowired + private TSysRoleMapper tSysRoleMapper; + + @Override + public List> getRoleSelectList() { + return tSysRoleMapper.getRoleSelectList(); + } +} diff --git a/src/main/java/com/freedom/admin/service/impl/TSysUserServiceImpl.java b/src/main/java/com/freedom/admin/service/impl/TSysUserServiceImpl.java new file mode 100644 index 0000000..8003d2b --- /dev/null +++ b/src/main/java/com/freedom/admin/service/impl/TSysUserServiceImpl.java @@ -0,0 +1,130 @@ +package com.freedom.admin.service.impl; + +import com.alibaba.fastjson.JSONArray; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.freedom.admin.config.MyException; +import com.freedom.admin.entity.TSysRole; +import com.freedom.admin.entity.TSysUser; +import com.freedom.admin.entity.TSysUserRole; +import com.freedom.admin.mapper.TSysRoleMapper; +import com.freedom.admin.mapper.TSysUserMapper; +import com.freedom.admin.mapper.TSysUserRoleMapper; +import com.freedom.admin.service.TSysUserService; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @author zhangjiaxun + * @date 2021/2/4 9:29 + * @description + */ +@Service +@Log4j2 +public class TSysUserServiceImpl extends ServiceImpl implements TSysUserService { + + @Autowired + private TSysUserMapper sysUserMapper; + + @Autowired + private TSysRoleMapper sysRoleMapper; + + @Autowired + private TSysUserRoleMapper sysUserRoleMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean save(TSysUser tSysUser, String userRoleStr, String operator) { + log.info("业务层-添加用户账号"); + BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); + String encode = bCryptPasswordEncoder.encode("123456"); + tSysUser.setEncodeType("BCrypt"); + tSysUser.setPassword(encode); + tSysUser.setCreateBy(operator); + tSysUser.setCreateDate(new Date()); + tSysUser.setUpdateBy(tSysUser.getCreateBy()); + tSysUser.setUpdateDate(tSysUser.getCreateDate()); + int insertTSysUser = sysUserMapper.insert(tSysUser); + log.info("业务层-添加用户账号"); + if (insertTSysUser <= 0){ + throw new MyException(400,"添加用户账号失败!"); + } + List tSysUserRoleList = JSONArray.parseArray(userRoleStr, TSysUserRole.class); + if (tSysUserRoleList ==null || tSysUserRoleList.size() == 0){ + throw new MyException(400,"添加用户权限为空!"); + } + int insertUserRoleCount = 0; + for (TSysUserRole tSysUserRole : tSysUserRoleList) { + tSysUserRole.setUserId(tSysUser.getId()); + tSysUserRole.setCreateBy(operator); + tSysUserRole.setCreateTime(new Date()); + int insertTSysUserRole = sysUserRoleMapper.insert(tSysUserRole); + insertUserRoleCount = insertUserRoleCount + insertTSysUserRole; + } + if (insertUserRoleCount != tSysUserRoleList.size()){ + throw new MyException(400,"添加用户账号的权限失败!"); + } + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean register(TSysUser sysUser) { + log.info("业务层-注册用户账号"); + BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); + String encode = bCryptPasswordEncoder.encode(sysUser.getPassword()); + sysUser.setEncodeType("BCrypt"); + sysUser.setPassword(encode); + sysUser.setCreateBy("注册页面"); + String []nickNameArr = { + "撸串研究生", + "一米五的小可爱", + "不解风情的老妖怪", + "自我否定先驱者", + "国家特级不保护动物", + "迪士尼在逃公主", + "资深网络表情包传播者", + "白日梦工厂厂长", + "内地十八线女明星", + "国家重度健忘症患者" + }; + int random=(int)(Math.random()*10); + sysUser.setNickname(nickNameArr[random]); + sysUser.setUpdateBy(sysUser.getCreateBy()); + sysUser.setUpdateDate(sysUser.getCreateDate()); + int insertSysUser = sysUserMapper.insert(sysUser); + log.info("业务层-添加用户账号"); + if (insertSysUser <= 0){ + throw new MyException(400,"添加用户账号失败!"); + } + QueryWrapper queryRoleWrapper = new QueryWrapper<>(); + queryRoleWrapper.eq("is_default", 1); + queryRoleWrapper.eq("del_flag", 0); + List sysRoleList = sysRoleMapper.selectList(queryRoleWrapper); + if (sysRoleList ==null || sysRoleList.size() == 0){ + throw new MyException(400,"添加用户权限为空,请管理员配置注册默认角色!"); + } + int insertUserRoleCount = 0; + for (TSysRole sysRole : sysRoleList) { + TSysUserRole sysUserRole = new TSysUserRole(); + sysUserRole.setRoleId(sysRole.getId()); + sysUserRole.setUserId(sysUser.getId()); + sysUserRole.setCreateBy("注册页面"); + sysUserRole.setCreateTime(new Date()); + int insertTSysUserRole = sysUserRoleMapper.insert(sysUserRole); + insertUserRoleCount = insertUserRoleCount + insertTSysUserRole; + } + if (insertUserRoleCount != sysRoleList.size()){ + throw new MyException(400,"添加用户账号的权限失败!"); + } + return true; + } + +} diff --git a/src/main/java/com/freedom/admin/service/impl/UserServiceImpl.java b/src/main/java/com/freedom/admin/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..df9f0d1 --- /dev/null +++ b/src/main/java/com/freedom/admin/service/impl/UserServiceImpl.java @@ -0,0 +1,56 @@ +package com.freedom.admin.service.impl; + +import com.freedom.admin.entity.TSysPermission; +import com.freedom.admin.entity.TSysUser; +import com.freedom.admin.mapper.TSysPermissionMapper; +import com.freedom.admin.mapper.TSysUserMapper; +import com.freedom.admin.service.UserService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + + +/** + * @author zhangjiaxun + * @date 2021/1/5 16:10 + * @description + */ + +@Service +public class UserServiceImpl implements UserService { + + @Autowired + private TSysUserMapper tSysUserMapper; + + @Autowired + private TSysPermissionMapper tSysPermissionMapper; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + TSysUser tSysUser = tSysUserMapper.selectOneByUsername(username); + List authorities = new ArrayList<>(); + if(tSysUser!=null){ + // 设置权限 + List tbPermissionList = tSysPermissionMapper.selectListByUserId(tSysUser.getId()); + tbPermissionList.forEach(tbPermission -> { + if (tbPermission!=null && StringUtils.isNotBlank(tbPermission.getPermissionCode())){ + GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(tbPermission.getPermissionCode()); + authorities.add(grantedAuthority); + } + }); + // 封装成UserDetails的实现类 + return new User(tSysUser.getUsername(),tSysUser.getPassword(),authorities); + }else { + throw new UsernameNotFoundException("用户名不存在"); + } + } + +} diff --git a/src/main/java/com/freedom/admin/utils/AlipayBean.java b/src/main/java/com/freedom/admin/utils/AlipayBean.java new file mode 100644 index 0000000..6a08ab3 --- /dev/null +++ b/src/main/java/com/freedom/admin/utils/AlipayBean.java @@ -0,0 +1,39 @@ +package com.freedom.admin.utils; + +import lombok.Data; + +/** + * @description: TODO + * @author: zhangjiaxun + * @date: 2021年03月13日 22:29:00 + */ +@Data +public class AlipayBean { + + /** + * 商户订单号,必填 + * */ + private String out_trade_no; + + /** + * 订单名称,必填 + * */ + private String subject; + + /** + * 订单名称,必填 + * */ + private StringBuffer total_amount; + + /** + * 商品描述,可空 + * */ + private String body; + + /** + * 超时时间参数 + * */ + private String timeout_express; + + private String product_code; +} diff --git a/src/main/java/com/freedom/admin/utils/JwtUtils.java b/src/main/java/com/freedom/admin/utils/JwtUtils.java new file mode 100644 index 0000000..8627d01 --- /dev/null +++ b/src/main/java/com/freedom/admin/utils/JwtUtils.java @@ -0,0 +1,75 @@ +package com.freedom.admin.utils; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.Date; + +/** + * @author zhangjiaxun + * @date 2021/2/1 10:06 + * @description JwtUtils + */ +@Component +public class JwtUtils { + + @Value("${jwt.secret}") + private String secret; + + @Value("${jwt.expiration}") + private Long expiration; + + /** + * 创建token + * + * @param username 用户名 + * @return token令牌 + * */ + public String generateToken(String username){ + return Jwts.builder() + .signWith(SignatureAlgorithm.HS512,secret) + .setSubject(username) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)) + .compact(); + } + + /** + * 获取body + * + * @param token 令牌 + * @return body包含您想要签署的任何信息 + * */ + public Claims getBody(String token){ + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token).getBody(); + } + + /** + * 是否已过期 + * + * @param token 令牌 + * @return 是否过期 + * */ + public boolean isExpiration(String token){ + return getBody(token) + .getExpiration() + .before(new Date()); + } + + /** + * 获取用户名 + * + * @param token 令牌 + * @return username用户名 + * */ + public String getUsernameFormToken(String token){ + return getBody(token) + .getSubject(); + } + +} diff --git a/src/main/java/com/freedom/admin/utils/SimilarityUtil.java b/src/main/java/com/freedom/admin/utils/SimilarityUtil.java new file mode 100644 index 0000000..a3bddf1 --- /dev/null +++ b/src/main/java/com/freedom/admin/utils/SimilarityUtil.java @@ -0,0 +1,73 @@ +package com.freedom.admin.utils; + +import java.util.ArrayList; +import java.util.List; + +/** + * @description: 相似度计算工具类 + * @author: zhangjiaxun + * @date: 2021年04月11日 16:07:00 + */ +public class SimilarityUtil { + + public static double similarity(List va, List vb) { + if (va.size() > vb.size()) { + int temp = va.size() - vb.size(); + for (int i = 0; i < temp; i++) { + vb.add(0.00); + } + + } else if (va.size() < vb.size()) { + int temp = vb.size() - va.size(); + for (int i = 0; i < temp; i++) { + va.add(0.00); + } + + } + + int size = va.size(); + + double simVal = 0.00; + double num = 0.00; + double den = 1.00; + double powaSum = 0.00; + double powbSum = 0.00; + + for (int i = 0; i < size; i++) { + double a = Double.parseDouble(va.get(i).toString()); + double b = Double.parseDouble(vb.get(i).toString()); + + num = num + a * b; + powaSum = powaSum + Math.pow(a, 2); + powbSum = powbSum + Math.pow(b, 2); + } + + double sqrta = Math.sqrt(powaSum); + double sqrtb = Math.sqrt(powbSum); + + den = sqrta * sqrtb; + simVal = num / den; + return simVal; + } + + public static void main(String[] args) { + double a[] = {(double) 4.5, 5, 5, 5,0}; + double b[] = {(double) 3.5, 5, 5, 5,0}; + + ArrayList vitem = new ArrayList(); + ArrayList va = new ArrayList(); + ArrayList vb = new ArrayList(); + for (int i = 0; i < a.length; i++){ + va.add(new Double(a[i])); + vb.add(new Double(b[i])); + } + + System.out.print("小红"); + System.out.println(va); + System.out.print("xxx"); + System.out.println(vb); + double simVal = SimilarityUtil.similarity(va, vb); + System.out.println("The sim value is:" + simVal); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..198d77a --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,73 @@ +#server.port +server: + port: 8800 + servlet: + context-path: / +#spring.datasource +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/delta?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC + username: root + password: 1Qaz@4772 + hikari: + initialization-fail-timeout: 1000 # 连接初始化失败超时时间 + minimum-idle: 5 + idle-timeout: 600000 + maximum-pool-size: 10 + auto-commit: true + pool-name: MyHikariCP + max-lifetime: 1800000 + connection-timeout: 30000 + connection-test-query: SELECT 1 + #email配置 + mail: + #smtp服务主机 qq邮箱则为smtp.qq.com + host: smtp.qq.com + #服务协议 + protocol: smtp + # 编码集 + default-encoding: UTF-8 + #发送邮件的账户 + username: 732128096@qq.com + #授权码 + password: nsbrkfdzzmdlbddi + #是否验证邮箱 + test-connection: false + properties: + mail: + smtp: + auth: true + starttls: + enable: true + required: true + #启动时不检查thymeleaf + thymeleaf: + check-template: false + check-template-location: false +#mybatis-plus +mybatis-plus: + mapper-locations: classpath:/mapper/*.xml + type-aliases-package: com.freedom.admin.entity + global-config: + db-config: + id-type: auto + logic-delete-field: delFlag + logic-delete-value: 1 + logic-not-delete-value: 0 + configuration: + map-underscore-to-camel-case: true + call-setters-on-nulls: true + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl +#分页插件 +pagehelper: + helper-dialect: mysql + reasonable: true + support-methods-arguments: true + params: count=countSql +#jwt +jwt: + header: Authorization #JWT存储的请求头 + secret: springsecurity-demo #JWT加解密使用的密钥 + expiration: 604800 #JWT的超期限时间(60*60*24*7) diff --git a/src/main/resources/btl/apiJs.btl b/src/main/resources/btl/apiJs.btl new file mode 100644 index 0000000..6a1fd21 --- /dev/null +++ b/src/main/resources/btl/apiJs.btl @@ -0,0 +1,50 @@ +import axios from '@/libs/api.request' +import {getToken} from "@/libs/util" + +/** 获取列表 **/ +export const getDataList = (data) => { + return axios.request({ + url: '${generatorConfig.resourceName}/findPageList', + headers: { + "Authorization": getToken() + }, + data: data, + method: 'post' + }) +} + +/** 新增 **/ +export const add = (data) => { + return axios.request({ + url: '${generatorConfig.resourceName}/add', + headers: { + "Authorization": getToken() + }, + data: data, + method: 'post' + }) +} + +/** 更新 **/ +export const update = (data) => { + return axios.request({ + url: '${generatorConfig.resourceName}/update', + headers: { + "Authorization": getToken() + }, + data: data, + method: 'post' + }) +} + +/** 删除 **/ +export const remove = (data) => { + return axios.request({ + url: '${generatorConfig.resourceName}/remove', + headers: { + "Authorization": getToken() + }, + data: data, + method: 'post' + }) +} diff --git a/src/main/resources/btl/controller.btl b/src/main/resources/btl/controller.btl new file mode 100644 index 0000000..c3bd2be --- /dev/null +++ b/src/main/resources/btl/controller.btl @@ -0,0 +1,98 @@ +package com.freedom.admin.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import ${generatorConfig.basePackage}.BaseController; +import com.freedom.admin.common.Result; +import ${generatorConfig.entityPackage}.${generatorConfig.className}; +import ${generatorConfig.servicePackage}.${generatorConfig.className}Service; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.annotations.Param; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.security.PermitAll; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * @author ${generatorConfig.author} + * @date ${generatorConfig.date} + * @description 控制层-${generatorConfig.description} + */ +@RestController +@RequestMapping("${generatorConfig.resourceName}") +@Log4j2 +public class ${generatorConfig.className}Controller extends BaseController { + + @Autowired + private ${generatorConfig.className}Service ${generatorConfig.classNameLowerCase}Service; + + /** + * 获取数据列表 + * */ + @RequestMapping("/findPageList") + @PermitAll + public Result<${generatorConfig.className}> findPageList(@Param("pageNum") long pageNum, + @Param("pageSize") long pageSize, + ${generatorConfig.className} ${generatorConfig.classNameLowerCase}){ + QueryWrapper<${generatorConfig.className}> queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("del_flag",0); + Page<${generatorConfig.className}> page = ${generatorConfig.classNameLowerCase}Service.page(new Page<>(pageNum,pageSize), queryWrapper); + return Result.ok().setData(page); + } + + /** + * 新增 + * */ + @RequestMapping("/add") + @PermitAll + public Result<${generatorConfig.className}> add(${generatorConfig.className} ${generatorConfig.classNameLowerCase}){ + try { + ${generatorConfig.classNameLowerCase}.setCreateBy(getUsername()); + ${generatorConfig.classNameLowerCase}.setCreateDate(new Date()); + boolean save = ${generatorConfig.classNameLowerCase}Service.save(${generatorConfig.classNameLowerCase}); + return Result.ok().setData(save); + } catch (Exception e) { + return Result.error(); + } + } + + /** + * 更新 + * */ + @RequestMapping("/update") + @PermitAll + public Result update(${generatorConfig.className} ${generatorConfig.classNameLowerCase}){ + try { + ${generatorConfig.classNameLowerCase}.setUpdateBy(getUsername()); + ${generatorConfig.classNameLowerCase}.setUpdateDate(new Date()); + boolean update = ${generatorConfig.classNameLowerCase}Service.updateById(${generatorConfig.classNameLowerCase}); + return Result.ok().setData(update); + } catch (Exception e) { + return Result.error(); + } + + } + + /** + * 删除 + * */ + @RequestMapping("/remove") + @PermitAll + public Result remove(${generatorConfig.className} ${generatorConfig.classNameLowerCase}){ + try { + ${generatorConfig.classNameLowerCase}.setUpdateBy(getUsername()); + ${generatorConfig.classNameLowerCase}.setUpdateDate(new Date()); + boolean remove = ${generatorConfig.classNameLowerCase}Service.removeById(${generatorConfig.classNameLowerCase}.getId()); + return Result.ok().setData(remove); + } catch (Exception e) { + return Result.error(); + } + + } + +} \ No newline at end of file diff --git a/src/main/resources/btl/entity.btl b/src/main/resources/btl/entity.btl new file mode 100644 index 0000000..1c60553 --- /dev/null +++ b/src/main/resources/btl/entity.btl @@ -0,0 +1,27 @@ +package com.freedom.admin.entity; + +import java.io.Serializable; +import java.util.Date; +import lombok.Data; + +/** + * @author ${generatorConfig.author} + * @date ${generatorConfig.date} + * @description ${generatorConfig.tableName} ${generatorConfig.description} + */ +@Data +public class ${generatorConfig.className} implements Serializable { +<% + for(rule in ruleList){ +%> + /** + * ${rule.variableComment} + * + */ + private ${rule.variableType} ${rule.variableName}; +<% + } +%> + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/resources/btl/mapper.btl b/src/main/resources/btl/mapper.btl new file mode 100644 index 0000000..40cccb5 --- /dev/null +++ b/src/main/resources/btl/mapper.btl @@ -0,0 +1,13 @@ +package com.freedom.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import ${generatorConfig.entityPackage}.${generatorConfig.className}; + +/** + * @author ${generatorConfig.author} + * @date ${generatorConfig.date} + * @description 数据访问层-${generatorConfig.description} +*/ +public interface ${generatorConfig.className}Mapper extends BaseMapper<${generatorConfig.className}> { + +} \ No newline at end of file diff --git a/src/main/resources/btl/mapperXml.btl b/src/main/resources/btl/mapperXml.btl new file mode 100644 index 0000000..a4354eb --- /dev/null +++ b/src/main/resources/btl/mapperXml.btl @@ -0,0 +1,30 @@ + + + + +<% + for(rule in ruleList){ +%> + +<% + } +%> + + + +<% + for(rule in ruleList){ + if(rule.index == ruleList.~size){ +%> + a.${rule.columnName} AS ${rule.variableName} +<% + }else{ +%> + a.${rule.columnName} AS ${rule.variableName}, +<% + } + } +%> + + + \ No newline at end of file diff --git a/src/main/resources/btl/service.btl b/src/main/resources/btl/service.btl new file mode 100644 index 0000000..f6802a7 --- /dev/null +++ b/src/main/resources/btl/service.btl @@ -0,0 +1,13 @@ +package com.freedom.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import ${generatorConfig.entityPackage}.${generatorConfig.className}; + +/** + * @author ${generatorConfig.author} + * @date ${generatorConfig.date} + * @description 业务接口层-${generatorConfig.description} + */ +public interface ${generatorConfig.className}Service extends IService<${generatorConfig.className}> { + +} diff --git a/src/main/resources/btl/serviceImpl.btl b/src/main/resources/btl/serviceImpl.btl new file mode 100644 index 0000000..5222b05 --- /dev/null +++ b/src/main/resources/btl/serviceImpl.btl @@ -0,0 +1,18 @@ +package com.freedom.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import ${generatorConfig.entityPackage}.${generatorConfig.className}; +import ${generatorConfig.mapperPackage}.${generatorConfig.className}Mapper; +import ${generatorConfig.servicePackage}.${generatorConfig.className}Service; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; + +/** + * @author ${generatorConfig.author} + * @date ${generatorConfig.date} + * @description 业务层实现-${generatorConfig.description} + */ +@Service +public class ${generatorConfig.className}ServiceImpl extends ServiceImpl<${generatorConfig.className}Mapper, ${generatorConfig.className}> implements ${generatorConfig.className}Service { + +} diff --git a/src/main/resources/btl/view.btl b/src/main/resources/btl/view.btl new file mode 100644 index 0000000..86b3c51 --- /dev/null +++ b/src/main/resources/btl/view.btl @@ -0,0 +1,350 @@ + + + + + diff --git a/src/main/resources/mapper/GeneratorMapper.xml b/src/main/resources/mapper/GeneratorMapper.xml new file mode 100644 index 0000000..240ccdf --- /dev/null +++ b/src/main/resources/mapper/GeneratorMapper.xml @@ -0,0 +1,37 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/TSysDeptMapper.xml b/src/main/resources/mapper/TSysDeptMapper.xml new file mode 100644 index 0000000..4007029 --- /dev/null +++ b/src/main/resources/mapper/TSysDeptMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + id, parent_id, dept_code, `dept_name`, `dept_desc`, `level`, sort, create_by, create_date, update_by, + update_date, del_flag + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/TSysPermissionMapper.xml b/src/main/resources/mapper/TSysPermissionMapper.xml new file mode 100644 index 0000000..a6b490c --- /dev/null +++ b/src/main/resources/mapper/TSysPermissionMapper.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + a.id, a.parent_id, a.permission_code, a.`permission_name`, a.`permission_desc`, a.`level`, a.icon, a.url, a.component, a.sort, a.create_by, + a.create_date, a.update_by, a.update_date, a.del_flag + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/TSysRoleMapper.xml b/src/main/resources/mapper/TSysRoleMapper.xml new file mode 100644 index 0000000..fad0ffc --- /dev/null +++ b/src/main/resources/mapper/TSysRoleMapper.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + id, role_name, role_code, role_desc, is_default, dept_id, sort, create_by, create_date, + update_by, update_date, del_flag + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/TSysRolePermissionMapper.xml b/src/main/resources/mapper/TSysRolePermissionMapper.xml new file mode 100644 index 0000000..3b62b67 --- /dev/null +++ b/src/main/resources/mapper/TSysRolePermissionMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + id, role_id, menu_id, create_by, create_date + + + \ No newline at end of file diff --git a/src/main/resources/mapper/TSysUserMapper.xml b/src/main/resources/mapper/TSysUserMapper.xml new file mode 100644 index 0000000..164307c --- /dev/null +++ b/src/main/resources/mapper/TSysUserMapper.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + id, username, `password`, encode_type, user_type, nickname, avatar, phone, email, + signature, status, create_by, create_date, update_by, update_date, del_flag + + + \ No newline at end of file diff --git a/src/main/resources/mapper/TSysUserRoleMapper.xml b/src/main/resources/mapper/TSysUserRoleMapper.xml new file mode 100644 index 0000000..41cba32 --- /dev/null +++ b/src/main/resources/mapper/TSysUserRoleMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + id, user_id, role_id, create_by, create_time + + + \ No newline at end of file diff --git a/src/test/java/com/freedom/admin/AdminApplicationTests.java b/src/test/java/com/freedom/admin/AdminApplicationTests.java new file mode 100644 index 0000000..f3bde0c --- /dev/null +++ b/src/test/java/com/freedom/admin/AdminApplicationTests.java @@ -0,0 +1,13 @@ +package com.freedom.admin; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AdminApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/代码评审文档.md b/代码评审文档.md new file mode 100644 index 0000000..8d1eef0 --- /dev/null +++ b/代码评审文档.md @@ -0,0 +1,264 @@ +# Freedom Admin 管理系统代码评审 + +## 项目概述 + +Freedom Admin 是一个基于 Spring Boot 的后台管理系统,采用 Spring Security 进行安全认证,MyBatis-Plus 作为 ORM 框架,集成代码生成器和邮件服务等功能。 + +## 代码结构分析 + +### 1. 项目结构 +``` +├── src/main/java/com/freedom/admin/ +│ ├── AdminApplication.java # 启动类 +│ ├── base/ # 基础控制器 +│ ├── common/ # 通用返回结果 +│ ├── config/ # 配置类 +│ ├── controller/ # 控制器层 +│ ├── entity/ # 实体类 +│ ├── mapper/ # 数据访问层 +│ ├── service/ # 服务层 +│ └── util/ # 工具类 +``` + +## 代码质量评估 + +### 1. 优点 + +#### 1.1 架构设计合理 +- ✅ 采用标准的 MVC 分层架构 +- ✅ 合理的包结构划分 +- ✅ 配置与业务代码分离 + +#### 1.2 安全实现完善 +- ✅ Spring Security + JWT 认证机制 +- ✅ 密码加密存储(BCrypt) +- ✅ 权限控制注解 + +#### 1.3 技术栈选择恰当 +- ✅ Spring Boot 2.7.10 稳定版本 +- ✅ MyBatis-Plus 简化数据操作 +- ✅ Thymeleaf 模板引擎 + +### 2. 问题与改进建议 + +#### 2.1 安全问题 + +**问题1:配置文件中的敏感信息** +```yaml +# src/main/resources/application.yml:13-14 +password: 1Qaz@4772 + +# src/main/resources/application.yml:35 +password: nsbrkfdzzmdlbddi +``` +**风险等级**:高危 +**建议**: +- 使用环境变量或配置中心管理敏感信息 +- 将密码等敏感数据移至安全位置 + +**问题2:硬编码的跨域配置** +```java +// src/main/java/com/freedom/admin/config/WebSecurityConfig.java:112 +cfg.addAllowedOrigin("http://localhost:8080"); +``` +**风险等级**:中危 +**建议**: +- 使用配置化方式管理允许的域名 +- 支持多环境配置 + +#### 2.2 代码规范问题 + +**问题3:空注释** +```java +// src/main/java/com/freedom/admin/config/WebSecurityConfig.java:21-23 +/** + * @author zhangjiaxun + * @date 2021/1/5 17:40 + * @description + */ +``` +**建议**:完善注释内容 + +**问题4:实体类缺少验证注解** +```java +// src/main/java/com/freedom/admin/entity/TSysUser.java +private String username; // 缺少 @NotBlank 等验证注解 +``` +**建议**:添加数据验证注解 + +#### 2.3 性能问题 + +**问题5:连接池配置可能过于保守** +```yaml +# src/main/resources/application.yml:18 +maximum-pool-size: 10 +``` +**建议**:根据实际使用场景调整连接池大小 + +## 详细代码分析 + +### 1. 安全配置分析 + +#### WebSecurityConfig.java 分析 + +**优点**: +- 完整的 JWT 认证流程 +- 合理的 CORS 配置 +- 密码加密器配置正确 + +**改进建议**: +```java +// 建议使用配置类管理允许的域名 +@Value("${cors.allowed-origins}") +private String[] allowedOrigins; + +// 在配置方法中使用 +cfg.addAllowedOrigin(allowedOrigins); +``` + +### 2. 实体类设计 + +#### TSysUser.java 分析 + +**优点**: +- 使用 Lombok 简化代码 +- 字段设计合理 +- 包含必要的审计字段 + +**改进建议**: +```java +// 添加数据验证注解 +import javax.validation.constraints.*; + +@NotBlank(message = "用户名不能为空") +@Size(min = 3, max = 50, message = "用户名长度必须在3-50之间") +private String username; + +@Email(message = "邮箱格式不正确") +private String email; +``` + +### 3. 配置文件分析 + +#### application.yml 分析 + +**优点**: +- 配置结构清晰 +- 支持多数据源配置 +- 包含邮件服务配置 + +**问题**: +- 敏感信息硬编码 +- 日志配置缺失 +- 缓存配置缺失 + +## 技术债务清单 + +### 高优先级 +1. **敏感信息管理** - 将密码等敏感数据移至安全位置 +2. **日志配置** - 添加完整的日志配置 +3. **数据验证** - 在实体类添加验证注解 + +### 中优先级 +1. **缓存机制** - 添加 Redis 缓存支持 +2. **API文档** - 集成 Swagger 文档 +3. **单元测试** - 补充单元测试覆盖 + +### 低优先级 +1. **代码注释** - 完善注释文档 +2. **配置优化** - 优化连接池等配置 +3. **错误处理** - 统一异常处理优化 + +## 性能优化建议 + +### 1. 数据库优化 +```java +// 建议添加分页查询优化 +@Configuration +public class MybatisPlusConfig { + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); + return interceptor; + } +} +``` + +### 2. 缓存优化 +```yaml +# 建议添加 Redis 缓存配置 +spring: + redis: + host: localhost + port: 6379 + password: ${REDIS_PASSWORD} + database: 0 +``` + +### 3. 异步处理 +```java +// 建议添加邮件发送异步处理 +@Async +public void sendEmailAsync(String to, String subject, String content) { + // 邮件发送逻辑 +} +``` + +## 安全加固建议 + +### 1. 输入验证 +```java +// 在 Controller 中添加输入验证 +@PostMapping("/register") +public Result register(@Valid @RequestBody UserRegisterDTO dto) { + // 业务逻辑 +} +``` + +### 2. SQL 注入防护 +- ✅ MyBatis-Plus 已提供 SQL 注入防护 +- 建议定期进行安全扫描 + +### 3. XSS 防护 +```java +// 建议添加 XSS 过滤器 +@Component +public class XssFilter implements Filter { + // XSS 防护逻辑 +} +``` + +## 部署建议 + +### 1. 环境分离 +```yaml +# 建议使用多环境配置 +spring: + profiles: + active: ${SPRING_PROFILES_ACTIVE:dev} +``` + +### 2. 健康检查 +```java +// 建议添加健康检查端点 +@Component +public class CustomHealthIndicator implements HealthIndicator { + @Override + public Health health() { + // 健康检查逻辑 + } +} +``` + +## 总结 + +Freedom Admin 项目整体架构设计合理,技术栈选择恰当,但在安全配置、代码规范和性能优化方面存在改进空间。建议优先处理高优先级的技术债务,特别是敏感信息管理和安全加固。 + +**总体评分**:7.5/10 + +**改进重点**: +1. 敏感信息安全管理 +2. 完善数据验证机制 +3. 添加缓存和性能优化 +4. 补充测试和文档 \ No newline at end of file diff --git a/新建文本文档.txt b/新建文本文档.txt new file mode 100644 index 0000000..e69de29 diff --git a/系统架构流程图.md b/系统架构流程图.md new file mode 100644 index 0000000..1275d08 --- /dev/null +++ b/系统架构流程图.md @@ -0,0 +1,226 @@ +# Freedom Admin 管理系统架构流程图 + +## 系统架构概览 + +```mermaid +graph TB + A[前端界面] -->|HTTP请求| B[Spring Boot 应用服务器] + B --> C[Spring Security] + B --> D[Spring MVC] + B --> E[Thymeleaf 模板引擎] + + C --> F[JWT 认证] + C --> G[权限控制] + + D --> H[Controller 层] + H --> I[Service 层] + I --> J[Mapper 层] + J --> K[MyBatis-Plus] + + E --> L[页面渲染] + + K --> M[MySQL 数据库] + B --> N[邮件服务] + N --> O[SMTP 服务器] + + B --> P[代码生成器] + P --> Q[Beetl 模板引擎] +``` + +## 详细架构流程 + +### 1. 用户访问流程 + +```mermaid +sequenceDiagram + participant U as 用户 + participant F as 前端界面 + participant GW as 安全网关 + participant C as Controller + participant S as Service + participant D as 数据库 + + U->>F: 访问系统 + F->>GW: HTTP请求 + GW->>GW: JWT认证检查 + GW->>C: 路由到对应Controller + C->>S: 调用业务逻辑 + S->>D: 数据库操作 + D->>S: 返回数据 + S->>C: 业务处理结果 + C->>F: 返回响应数据 + F->>U: 页面渲染展示 +``` + +### 2. 安全认证流程 + +```mermaid +flowchart TD + Start[用户登录] --> Input[输入用户名密码] + Input --> Validate{格式验证} + Validate -->|格式错误| Error1[返回错误信息] + Validate -->|格式正确| Auth[Spring Security认证] + Auth --> DB[查询数据库验证] + DB -->|验证失败| Error2[认证失败] + DB -->|验证成功| JWT[生成JWT Token] + JWT --> Success[登录成功] + Success --> SetToken[设置Token到响应头] + SetToken --> Redirect[重定向到首页] +``` + +### 3. 请求处理流程 + +```mermaid +graph LR + A[HTTP请求] --> B[DispatcherServlet] + B --> C[HandlerMapping] + C --> D[Controller] + D --> E[Service] + E --> F[Mapper/MyBatis-Plus] + F --> G[MySQL数据库] + G --> F + F --> E + E --> D + D --> H[ViewResolver] + H --> I[Thymeleaf模板] + I --> J[HTTP响应] +``` + +### 4. 数据流架构 + +```mermaid +graph TD + A[前端UI层] -->|JSON/Form Data| B[Controller控制层] + B -->|Service接口| C[Service业务层] + C -->|Mapper接口| D[数据访问层] + D -->|SQL映射| E[MySQL数据库] + + F[代码生成器] -->|Beetl模板| G[生成代码文件] + G --> H[Controller/Service/Entity] + + I[邮件服务] -->|SMTP协议| J[邮件服务器] +``` + +## 核心组件架构 + +### 1. 认证授权架构 + +```mermaid +graph TB + subgraph "安全认证模块" + A[JWT认证过滤器] --> B[Spring Security] + B --> C[UserDetailsService] + C --> D[密码加密器] + B --> E[权限决策器] + E --> F[角色权限映射] + end + + subgraph "数据库层" + G[用户表] --> H[角色表] + H --> I[权限表] + G --> J[用户角色关联] + H --> K[角色权限关联] + end + + A --> G + E --> I +``` + +### 2. 业务模块架构 + +```mermaid +graph LR + subgraph "用户管理模块" + A1[用户注册] --> A2[用户登录] + A2 --> A3[权限管理] + A3 --> A4[个人信息] + end + + subgraph "代码生成模块" + B1[模板管理] --> B2[代码生成] + B2 --> B3[文件输出] + end + + subgraph "邮件服务模块" + C1[邮件模板] --> C2[邮件发送] + C2 --> C3[发送记录] + end + + subgraph "数据管理模块" + D1[部门管理] --> D2[活动管理] + D2 --> D3[药品管理] + D3 --> D4[订单管理] + end +``` + +### 3. 技术栈架构 + +```mermaid +graph TB + subgraph "前端技术栈" + A1[HTML5] --> A2[CSS3] + A2 --> A3[JavaScript] + A3 --> A4[Thymeleaf] + end + + subgraph "后端技术栈" + B1[Spring Boot 2.7.10] --> B2[Spring Security] + B2 --> B3[MyBatis-Plus] + B3 --> B4[HikariCP] + end + + subgraph "安全技术栈" + C1[JWT Token] --> C2[BCrypt加密] + C2 --> C3[权限控制] + end + + subgraph "工具技术栈" + D1[Beetl模板] --> D2[邮件服务] + D2 --> D3[代码生成] + end + + A4 --> B1 + B4 --> C1 + D3 --> B1 +``` + +## 部署架构 + +```mermaid +graph TB + subgraph "应用服务器" + A[Spring Boot应用] --> B[Tomcat容器] + B --> C[JVM运行时] + end + + subgraph "数据库服务器" + D[MySQL 8.0] --> E[数据存储] + E --> F[连接池] + end + + subgraph "外部服务" + G[邮件服务器] --> H[SMTP服务] + I[前端资源] --> J[静态文件] + end + + A --> D + A --> G + A --> I +``` + +## 配置文件架构 + +```mermaid +graph LR + A[application.yml] --> B[数据源配置] + A --> C[安全配置] + A --> D[邮件配置] + A --> E[MyBatis配置] + + B --> F[数据库连接] + C --> G[JWT参数] + D --> H[SMTP设置] + E --> I[映射配置] +``` + +这个架构流程图展示了 Freedom Admin 管理系统的完整技术架构和业务流程,包括前端到后端的完整请求处理流程、安全认证机制、模块化设计以及技术栈构成。 \ No newline at end of file