From 4fe7ba5d0d781c47b3a2c293f223810036568981 Mon Sep 17 00:00:00 2001 From: panxuejie <15855548138@163.com> Date: Tue, 30 Dec 2025 11:14:25 +0800 Subject: [PATCH] Initial commit --- pom.xml | 150 ++++++++++++++ tacit-admin/pom.xml | 101 +++++++++ .../com/tacit/admin/AdminApplication.java | 17 ++ .../tacit/admin/config/SecurityConfig.java | 114 ++++++++++ .../com/tacit/admin/config/SwaggerConfig.java | 23 ++ .../admin/controller/TestController.java | 32 +++ .../admin/controller/UserController.java | 70 +++++++ .../java/com/tacit/admin/entity/User.java | 28 +++ .../tacit/admin/entity/dto/LoginRequest.java | 23 ++ .../tacit/admin/entity/dto/LoginResponse.java | 24 +++ .../com/tacit/admin/mapper/UserMapper.java | 9 + .../com/tacit/admin/service/UserService.java | 58 ++++++ .../admin/service/impl/UserServiceImpl.java | 98 +++++++++ .../src/main/resources/application-dev.yml | 30 +++ tacit-admin/src/main/resources/bootstrap.yml | 16 ++ .../target/classes/application-dev.yml | 30 +++ tacit-admin/target/classes/bootstrap.yml | 16 ++ .../com/tacit/admin/AdminApplication.class | Bin 0 -> 930 bytes .../tacit/admin/config/SecurityConfig$1.class | Bin 0 -> 4430 bytes .../tacit/admin/config/SecurityConfig.class | Bin 0 -> 8399 bytes .../tacit/admin/config/SwaggerConfig.class | Bin 0 -> 993 bytes .../admin/controller/TestController.class | Bin 0 -> 1872 bytes .../admin/controller/UserController.class | Bin 0 -> 4220 bytes .../classes/com/tacit/admin/entity/User.class | Bin 0 -> 6753 bytes .../tacit/admin/entity/dto/LoginRequest.class | Bin 0 -> 2429 bytes .../admin/entity/dto/LoginResponse.class | Bin 0 -> 2571 bytes .../com/tacit/admin/mapper/UserMapper.class | Bin 0 -> 381 bytes .../com/tacit/admin/service/UserService.class | Bin 0 -> 790 bytes .../admin/service/impl/UserServiceImpl.class | Bin 0 -> 4374 bytes tacit-app-api/pom.xml | 101 +++++++++ .../java/com/tacit/app/AppApiApplication.java | 17 ++ .../com/tacit/app/config/SwaggerConfig.java | 23 ++ .../tacit/app/controller/AuthController.java | 49 +++++ .../tacit/app/controller/UserController.java | 35 ++++ .../main/java/com/tacit/app/entity/User.java | 28 +++ .../tacit/app/entity/dto/LoginRequest.java | 9 + .../tacit/app/entity/dto/LoginResponse.java | 11 + .../java/com/tacit/app/mapper/UserMapper.java | 9 + .../com/tacit/app/service/UserService.java | 36 ++++ .../app/service/impl/UserServiceImpl.java | 115 ++++++++++ .../src/main/resources/application-dev.yml | 30 +++ .../src/main/resources/bootstrap.yml | 16 ++ .../app/service/impl/UserServiceImplTest.java | 196 ++++++++++++++++++ .../target/classes/application-dev.yml | 30 +++ tacit-app-api/target/classes/bootstrap.yml | 16 ++ .../com/tacit/app/AppApiApplication.class | Bin 0 -> 927 bytes .../com/tacit/app/config/SwaggerConfig.class | Bin 0 -> 988 bytes .../tacit/app/controller/AuthController.class | Bin 0 -> 2980 bytes .../tacit/app/controller/UserController.class | Bin 0 -> 2377 bytes .../classes/com/tacit/app/entity/User.class | Bin 0 -> 6749 bytes .../tacit/app/entity/dto/LoginRequest.class | Bin 0 -> 2174 bytes .../tacit/app/entity/dto/LoginResponse.class | Bin 0 -> 2813 bytes .../com/tacit/app/mapper/UserMapper.class | Bin 0 -> 377 bytes .../com/tacit/app/service/UserService.class | Bin 0 -> 554 bytes .../app/service/impl/UserServiceImpl.class | Bin 0 -> 4596 bytes tacit-common/pom.xml | 98 +++++++++ .../tacit/common/constant/CommonConstant.java | 27 +++ .../tacit/common/entity/ResponseResult.java | 47 +++++ .../common/exception/BusinessException.java | 34 +++ .../tacit/common/feign/AdminFeignClient.java | 13 ++ .../tacit/common/feign/AppApiFeignClient.java | 13 ++ .../handler/GlobalExceptionHandler.java | 72 +++++++ .../java/com/tacit/common/utils/JwtUtils.java | 112 ++++++++++ .../common/entity/ResponseResultTest.java | 82 ++++++++ .../com/tacit/common/utils/JwtUtilsTest.java | 75 +++++++ .../common/constant/CommonConstant.class | Bin 0 -> 1190 bytes .../tacit/common/entity/ResponseResult.class | Bin 0 -> 4973 bytes .../common/exception/BusinessException.class | Bin 0 -> 1269 bytes .../tacit/common/feign/AdminFeignClient.class | Bin 0 -> 692 bytes .../common/feign/AppApiFeignClient.class | Bin 0 -> 691 bytes .../handler/GlobalExceptionHandler.class | Bin 0 -> 4659 bytes .../com/tacit/common/utils/JwtUtils.class | Bin 0 -> 4729 bytes tacit-gateway/pom.xml | 79 +++++++ .../com/tacit/gateway/GatewayApplication.java | 13 ++ .../filter/JwtAuthenticationFilter.java | 104 ++++++++++ .../src/main/resources/application-dev.yml | 50 +++++ .../src/main/resources/bootstrap.yml | 16 ++ .../target/classes/application-dev.yml | 50 +++++ tacit-gateway/target/classes/bootstrap.yml | 16 ++ .../tacit/gateway/GatewayApplication.class | Bin 0 -> 787 bytes .../filter/JwtAuthenticationFilter.class | Bin 0 -> 6979 bytes 81 files changed, 2461 insertions(+) create mode 100644 pom.xml create mode 100644 tacit-admin/pom.xml create mode 100644 tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java create mode 100644 tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java create mode 100644 tacit-admin/src/main/java/com/tacit/admin/config/SwaggerConfig.java create mode 100644 tacit-admin/src/main/java/com/tacit/admin/controller/TestController.java create mode 100644 tacit-admin/src/main/java/com/tacit/admin/controller/UserController.java create mode 100644 tacit-admin/src/main/java/com/tacit/admin/entity/User.java create mode 100644 tacit-admin/src/main/java/com/tacit/admin/entity/dto/LoginRequest.java create mode 100644 tacit-admin/src/main/java/com/tacit/admin/entity/dto/LoginResponse.java create mode 100644 tacit-admin/src/main/java/com/tacit/admin/mapper/UserMapper.java create mode 100644 tacit-admin/src/main/java/com/tacit/admin/service/UserService.java create mode 100644 tacit-admin/src/main/java/com/tacit/admin/service/impl/UserServiceImpl.java create mode 100644 tacit-admin/src/main/resources/application-dev.yml create mode 100644 tacit-admin/src/main/resources/bootstrap.yml create mode 100644 tacit-admin/target/classes/application-dev.yml create mode 100644 tacit-admin/target/classes/bootstrap.yml create mode 100644 tacit-admin/target/classes/com/tacit/admin/AdminApplication.class create mode 100644 tacit-admin/target/classes/com/tacit/admin/config/SecurityConfig$1.class create mode 100644 tacit-admin/target/classes/com/tacit/admin/config/SecurityConfig.class create mode 100644 tacit-admin/target/classes/com/tacit/admin/config/SwaggerConfig.class create mode 100644 tacit-admin/target/classes/com/tacit/admin/controller/TestController.class create mode 100644 tacit-admin/target/classes/com/tacit/admin/controller/UserController.class create mode 100644 tacit-admin/target/classes/com/tacit/admin/entity/User.class create mode 100644 tacit-admin/target/classes/com/tacit/admin/entity/dto/LoginRequest.class create mode 100644 tacit-admin/target/classes/com/tacit/admin/entity/dto/LoginResponse.class create mode 100644 tacit-admin/target/classes/com/tacit/admin/mapper/UserMapper.class create mode 100644 tacit-admin/target/classes/com/tacit/admin/service/UserService.class create mode 100644 tacit-admin/target/classes/com/tacit/admin/service/impl/UserServiceImpl.class create mode 100644 tacit-app-api/pom.xml create mode 100644 tacit-app-api/src/main/java/com/tacit/app/AppApiApplication.java create mode 100644 tacit-app-api/src/main/java/com/tacit/app/config/SwaggerConfig.java create mode 100644 tacit-app-api/src/main/java/com/tacit/app/controller/AuthController.java create mode 100644 tacit-app-api/src/main/java/com/tacit/app/controller/UserController.java create mode 100644 tacit-app-api/src/main/java/com/tacit/app/entity/User.java create mode 100644 tacit-app-api/src/main/java/com/tacit/app/entity/dto/LoginRequest.java create mode 100644 tacit-app-api/src/main/java/com/tacit/app/entity/dto/LoginResponse.java create mode 100644 tacit-app-api/src/main/java/com/tacit/app/mapper/UserMapper.java create mode 100644 tacit-app-api/src/main/java/com/tacit/app/service/UserService.java create mode 100644 tacit-app-api/src/main/java/com/tacit/app/service/impl/UserServiceImpl.java create mode 100644 tacit-app-api/src/main/resources/application-dev.yml create mode 100644 tacit-app-api/src/main/resources/bootstrap.yml create mode 100644 tacit-app-api/src/test/java/com/tacit/app/service/impl/UserServiceImplTest.java create mode 100644 tacit-app-api/target/classes/application-dev.yml create mode 100644 tacit-app-api/target/classes/bootstrap.yml create mode 100644 tacit-app-api/target/classes/com/tacit/app/AppApiApplication.class create mode 100644 tacit-app-api/target/classes/com/tacit/app/config/SwaggerConfig.class create mode 100644 tacit-app-api/target/classes/com/tacit/app/controller/AuthController.class create mode 100644 tacit-app-api/target/classes/com/tacit/app/controller/UserController.class create mode 100644 tacit-app-api/target/classes/com/tacit/app/entity/User.class create mode 100644 tacit-app-api/target/classes/com/tacit/app/entity/dto/LoginRequest.class create mode 100644 tacit-app-api/target/classes/com/tacit/app/entity/dto/LoginResponse.class create mode 100644 tacit-app-api/target/classes/com/tacit/app/mapper/UserMapper.class create mode 100644 tacit-app-api/target/classes/com/tacit/app/service/UserService.class create mode 100644 tacit-app-api/target/classes/com/tacit/app/service/impl/UserServiceImpl.class create mode 100644 tacit-common/pom.xml create mode 100644 tacit-common/src/main/java/com/tacit/common/constant/CommonConstant.java create mode 100644 tacit-common/src/main/java/com/tacit/common/entity/ResponseResult.java create mode 100644 tacit-common/src/main/java/com/tacit/common/exception/BusinessException.java create mode 100644 tacit-common/src/main/java/com/tacit/common/feign/AdminFeignClient.java create mode 100644 tacit-common/src/main/java/com/tacit/common/feign/AppApiFeignClient.java create mode 100644 tacit-common/src/main/java/com/tacit/common/handler/GlobalExceptionHandler.java create mode 100644 tacit-common/src/main/java/com/tacit/common/utils/JwtUtils.java create mode 100644 tacit-common/src/test/java/com/tacit/common/entity/ResponseResultTest.java create mode 100644 tacit-common/src/test/java/com/tacit/common/utils/JwtUtilsTest.java create mode 100644 tacit-common/target/classes/com/tacit/common/constant/CommonConstant.class create mode 100644 tacit-common/target/classes/com/tacit/common/entity/ResponseResult.class create mode 100644 tacit-common/target/classes/com/tacit/common/exception/BusinessException.class create mode 100644 tacit-common/target/classes/com/tacit/common/feign/AdminFeignClient.class create mode 100644 tacit-common/target/classes/com/tacit/common/feign/AppApiFeignClient.class create mode 100644 tacit-common/target/classes/com/tacit/common/handler/GlobalExceptionHandler.class create mode 100644 tacit-common/target/classes/com/tacit/common/utils/JwtUtils.class create mode 100644 tacit-gateway/pom.xml create mode 100644 tacit-gateway/src/main/java/com/tacit/gateway/GatewayApplication.java create mode 100644 tacit-gateway/src/main/java/com/tacit/gateway/filter/JwtAuthenticationFilter.java create mode 100644 tacit-gateway/src/main/resources/application-dev.yml create mode 100644 tacit-gateway/src/main/resources/bootstrap.yml create mode 100644 tacit-gateway/target/classes/application-dev.yml create mode 100644 tacit-gateway/target/classes/bootstrap.yml create mode 100644 tacit-gateway/target/classes/com/tacit/gateway/GatewayApplication.class create mode 100644 tacit-gateway/target/classes/com/tacit/gateway/filter/JwtAuthenticationFilter.class diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..69fcbcd --- /dev/null +++ b/pom.xml @@ -0,0 +1,150 @@ + + + 4.0.0 + + com.tacit + tacit-parent + 1.0.0-SNAPSHOT + pom + + Tacit Parent + Tacit App Microservice Architecture Parent Project + + + 21 + 3.2.0 + 2023.0.0 + 2023.0.1.0 + 3.5.5 + 1.18.30 + 0.11.5 + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring-cloud-alibaba.version} + pom + import + + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + + org.projectlombok + lombok + ${lombok.version} + + + + + io.jsonwebtoken + jjwt-api + ${jjwt.version} + + + io.jsonwebtoken + jjwt-impl + ${jjwt.version} + runtime + + + io.jsonwebtoken + jjwt-jackson + ${jjwt.version} + runtime + + + + + + tacit-gateway + tacit-admin + tacit-app-api + tacit-common + + + + + central + Maven Central Repository + https://repo1.maven.org/maven2/ + default + + true + + + false + + + + + + + central + Maven Central Repository + https://repo1.maven.org/maven2/ + default + + true + + + false + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + ${java.version} + ${java.version} + UTF-8 + + + + + + + \ No newline at end of file diff --git a/tacit-admin/pom.xml b/tacit-admin/pom.xml new file mode 100644 index 0000000..86dee2b --- /dev/null +++ b/tacit-admin/pom.xml @@ -0,0 +1,101 @@ + + + + tacit-parent + com.tacit + 1.0.0-SNAPSHOT + + 4.0.0 + + tacit-admin + Tacit Admin + Admin Service for Tacit Microservices + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-security + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.baomidou + mybatis-plus-boot-starter + + + + + com.oceanbase + oceanbase-client + 2.4.0 + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.3.0 + + + + + com.tacit + tacit-common + ${project.parent.version} + + + + + org.projectlombok + lombok + provided + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + false + + + + + \ No newline at end of file diff --git a/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java b/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java new file mode 100644 index 0000000..cf6e603 --- /dev/null +++ b/tacit-admin/src/main/java/com/tacit/admin/AdminApplication.java @@ -0,0 +1,17 @@ +package com.tacit.admin; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; + +@SpringBootApplication +@EnableDiscoveryClient +@EnableFeignClients +@MapperScan("com.tacit.admin.mapper") +public class AdminApplication { + public static void main(String[] args) { + SpringApplication.run(AdminApplication.class, args); + } +} diff --git a/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java b/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java new file mode 100644 index 0000000..55841d0 --- /dev/null +++ b/tacit-admin/src/main/java/com/tacit/admin/config/SecurityConfig.java @@ -0,0 +1,114 @@ +package com.tacit.admin.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.filter.OncePerRequestFilter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Collections; + +@Configuration +@EnableWebSecurity +@EnableMethodSecurity(prePostEnabled = true) +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + // 禁用CSRF保护 + .csrf(csrf -> csrf.disable()) + // 允许跨域请求 + .cors(cors -> cors.disable()) + // 设置会话管理为无状态 + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + // 添加JWT认证过滤器 + .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) + // 设置请求授权规则 + .authorizeHttpRequests(auth -> auth + // 允许访问的路径 + .requestMatchers("/test/**", "/user/login", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll() + // 其他请求需要认证 + .anyRequest().authenticated() + ); + + return http.build(); + } + + @Bean + public OncePerRequestFilter jwtAuthenticationFilter() { + return new OncePerRequestFilter() { + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + // 从请求头中获取用户信息(由网关添加) + String userId = request.getHeader("X-User-Id"); + String username = request.getHeader("X-Username"); + String role = request.getHeader("X-Role"); + + // 如果有用户信息,则创建Authentication对象并设置到SecurityContext + if (userId != null && username != null && role != null) { + // 创建用户对象 + org.springframework.security.core.userdetails.User user = new User( + username, + "", + Collections.singletonList(() -> "ROLE_" + role.toUpperCase()) + ); + + // 创建认证对象 + org.springframework.security.authentication.UsernamePasswordAuthenticationToken authentication = + new org.springframework.security.authentication.UsernamePasswordAuthenticationToken( + user, + null, + user.getAuthorities() + ); + + // 设置认证信息到SecurityContext + org.springframework.security.core.context.SecurityContextHolder.getContext().setAuthentication(authentication); + } + + // 继续过滤链 + filterChain.doFilter(request, response); + } + }; + } + + @Bean + public AuthenticationManager authenticationManager(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setUserDetailsService(userDetailsService); + provider.setPasswordEncoder(passwordEncoder); + return new ProviderManager(Collections.singletonList(provider)); + } + + @Bean + public UserDetailsService userDetailsService() { + // 这里可以根据实际情况实现UserDetailsService,从数据库中获取用户信息 + return username -> { + // 由于我们使用网关进行认证,这里可以返回一个空实现 + // 实际项目中应该根据用户名从数据库中获取用户信息 + throw new UsernameNotFoundException("User not found: " + username); + }; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/tacit-admin/src/main/java/com/tacit/admin/config/SwaggerConfig.java b/tacit-admin/src/main/java/com/tacit/admin/config/SwaggerConfig.java new file mode 100644 index 0000000..c233c2a --- /dev/null +++ b/tacit-admin/src/main/java/com/tacit/admin/config/SwaggerConfig.java @@ -0,0 +1,23 @@ +package com.tacit.admin.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Swagger配置类 + * 使用SpringDoc OpenAPI配置API文档 + */ +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI adminOpenAPI() { + return new OpenAPI() + .info(new Info() + .title("Tacit Admin API") + .description("管理台服务API文档") + .version("1.0.0")); + } +} diff --git a/tacit-admin/src/main/java/com/tacit/admin/controller/TestController.java b/tacit-admin/src/main/java/com/tacit/admin/controller/TestController.java new file mode 100644 index 0000000..d885e0d --- /dev/null +++ b/tacit-admin/src/main/java/com/tacit/admin/controller/TestController.java @@ -0,0 +1,32 @@ +package com.tacit.admin.controller; + +import com.tacit.common.entity.ResponseResult; +import com.tacit.common.feign.AppApiFeignClient; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/test") +@Tag(name = "测试接口", description = "用于测试的接口") +public class TestController { + + @Autowired + private AppApiFeignClient appApiFeignClient; + + @Operation(summary = "测试接口", description = "这是一个测试接口") + @GetMapping("/hello") + public ResponseResult hello() { + return ResponseResult.success("Hello from admin service!"); + } + + @Operation(summary = "测试Feign调用", description = "测试通过Feign调用APP接口服务") + @GetMapping("/feign/{userId}") + public ResponseResult testFeign(@PathVariable Long userId) { + return appApiFeignClient.getUserInfo(userId); + } +} diff --git a/tacit-admin/src/main/java/com/tacit/admin/controller/UserController.java b/tacit-admin/src/main/java/com/tacit/admin/controller/UserController.java new file mode 100644 index 0000000..9752e5b --- /dev/null +++ b/tacit-admin/src/main/java/com/tacit/admin/controller/UserController.java @@ -0,0 +1,70 @@ +package com.tacit.admin.controller; + +import com.tacit.admin.entity.User; +import com.tacit.admin.entity.dto.LoginRequest; +import com.tacit.admin.entity.dto.LoginResponse; +import com.tacit.admin.service.UserService; +import com.tacit.common.entity.ResponseResult; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/user") +@Tag(name = "用户管理", description = "用户相关接口") +public class UserController { + + @Autowired + private UserService userService; + + @Operation(summary = "用户登录", description = "用户登录获取JWT令牌") + @PostMapping("/login") + public ResponseResult login(@RequestBody LoginRequest loginRequest) { + LoginResponse loginResponse = userService.login(loginRequest); + return ResponseResult.success(loginResponse); + } + + @Operation(summary = "获取所有用户", description = "获取系统中所有用户列表") + @GetMapping("/list") + @PreAuthorize("hasRole('ADMIN')") + public ResponseResult> getAllUsers() { + List users = userService.getAllUsers(); + return ResponseResult.success(users); + } + + @Operation(summary = "根据ID获取用户", description = "根据用户ID获取用户详情") + @GetMapping("/info/{id}") + @PreAuthorize("hasRole('ADMIN')") + public ResponseResult getUserById(@PathVariable Long id) { + User user = userService.getUserById(id); + return ResponseResult.success(user); + } + + @Operation(summary = "创建用户", description = "创建新用户") + @PostMapping("/create") + @PreAuthorize("hasRole('ADMIN')") + public ResponseResult createUser(@RequestBody User user) { + boolean result = userService.createUser(user); + return ResponseResult.success(result); + } + + @Operation(summary = "更新用户", description = "更新用户信息") + @PutMapping("/update") + @PreAuthorize("hasRole('ADMIN')") + public ResponseResult updateUser(@RequestBody User user) { + boolean result = userService.updateUser(user); + return ResponseResult.success(result); + } + + @Operation(summary = "删除用户", description = "根据用户ID删除用户") + @DeleteMapping("/delete/{id}") + @PreAuthorize("hasRole('ADMIN')") + public ResponseResult deleteUser(@PathVariable Long id) { + boolean result = userService.deleteUser(id); + return ResponseResult.success(result); + } +} diff --git a/tacit-admin/src/main/java/com/tacit/admin/entity/User.java b/tacit-admin/src/main/java/com/tacit/admin/entity/User.java new file mode 100644 index 0000000..d210d9c --- /dev/null +++ b/tacit-admin/src/main/java/com/tacit/admin/entity/User.java @@ -0,0 +1,28 @@ +package com.tacit.admin.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Data +@TableName("t_user") +public class User implements Serializable { + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Long id; + private String username; + private String password; + private String nickname; + private String email; + private String phone; + private Integer status; + private String role; + private LocalDateTime createTime; + private LocalDateTime updateTime; + private Integer delFlag; +} diff --git a/tacit-admin/src/main/java/com/tacit/admin/entity/dto/LoginRequest.java b/tacit-admin/src/main/java/com/tacit/admin/entity/dto/LoginRequest.java new file mode 100644 index 0000000..6fbb576 --- /dev/null +++ b/tacit-admin/src/main/java/com/tacit/admin/entity/dto/LoginRequest.java @@ -0,0 +1,23 @@ +package com.tacit.admin.entity.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 登录请求DTO + */ +@Data +public class LoginRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 用户名 + */ + private String username; + + /** + * 密码 + */ + private String password; +} \ No newline at end of file diff --git a/tacit-admin/src/main/java/com/tacit/admin/entity/dto/LoginResponse.java b/tacit-admin/src/main/java/com/tacit/admin/entity/dto/LoginResponse.java new file mode 100644 index 0000000..4e15692 --- /dev/null +++ b/tacit-admin/src/main/java/com/tacit/admin/entity/dto/LoginResponse.java @@ -0,0 +1,24 @@ +package com.tacit.admin.entity.dto; + +import com.tacit.admin.entity.User; +import lombok.Data; + +import java.io.Serializable; + +/** + * 登录响应DTO + */ +@Data +public class LoginResponse implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * JWT令牌 + */ + private String token; + + /** + * 用户信息 + */ + private User user; +} \ No newline at end of file diff --git a/tacit-admin/src/main/java/com/tacit/admin/mapper/UserMapper.java b/tacit-admin/src/main/java/com/tacit/admin/mapper/UserMapper.java new file mode 100644 index 0000000..9598556 --- /dev/null +++ b/tacit-admin/src/main/java/com/tacit/admin/mapper/UserMapper.java @@ -0,0 +1,9 @@ +package com.tacit.admin.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.tacit.admin.entity.User; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface UserMapper extends BaseMapper { +} diff --git a/tacit-admin/src/main/java/com/tacit/admin/service/UserService.java b/tacit-admin/src/main/java/com/tacit/admin/service/UserService.java new file mode 100644 index 0000000..81c1667 --- /dev/null +++ b/tacit-admin/src/main/java/com/tacit/admin/service/UserService.java @@ -0,0 +1,58 @@ +package com.tacit.admin.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.tacit.admin.entity.User; +import com.tacit.admin.entity.dto.LoginRequest; +import com.tacit.admin.entity.dto.LoginResponse; + +import java.util.List; + +public interface UserService extends IService { + /** + * 根据用户名查询用户 + * @param username 用户名 + * @return 用户信息 + */ + User getUserByUsername(String username); + + /** + * 根据用户ID查询用户 + * @param id 用户ID + * @return 用户信息 + */ + User getUserById(Long id); + + /** + * 获取所有用户列表 + * @return 用户列表 + */ + List getAllUsers(); + + /** + * 创建用户 + * @param user 用户信息 + * @return 创建结果 + */ + boolean createUser(User user); + + /** + * 更新用户信息 + * @param user 用户信息 + * @return 更新结果 + */ + boolean updateUser(User user); + + /** + * 删除用户 + * @param id 用户ID + * @return 删除结果 + */ + boolean deleteUser(Long id); + + /** + * 用户登录 + * @param loginRequest 登录请求参数 + * @return 登录响应结果 + */ + LoginResponse login(LoginRequest loginRequest); +} diff --git a/tacit-admin/src/main/java/com/tacit/admin/service/impl/UserServiceImpl.java b/tacit-admin/src/main/java/com/tacit/admin/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..efa24f4 --- /dev/null +++ b/tacit-admin/src/main/java/com/tacit/admin/service/impl/UserServiceImpl.java @@ -0,0 +1,98 @@ +package com.tacit.admin.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.tacit.admin.entity.User; +import com.tacit.admin.entity.dto.LoginRequest; +import com.tacit.admin.entity.dto.LoginResponse; +import com.tacit.admin.mapper.UserMapper; +import com.tacit.admin.service.UserService; +import com.tacit.common.utils.JwtUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class UserServiceImpl extends ServiceImpl implements UserService { + + @Autowired + private UserMapper userMapper; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Override + public User getUserByUsername(String username) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("username", username) + .eq("del_flag", 0); + return userMapper.selectOne(queryWrapper); + } + + @Override + public User getUserById(Long id) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("id", id) + .eq("del_flag", 0); + return userMapper.selectOne(queryWrapper); + } + + @Override + public List getAllUsers() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("del_flag", 0) + .orderByDesc("create_time"); + return userMapper.selectList(queryWrapper); + } + + @Override + public boolean createUser(User user) { + user.setDelFlag(0); + return save(user); + } + + @Override + public boolean updateUser(User user) { + return updateById(user); + } + + @Override + public boolean deleteUser(Long id) { + User user = new User(); + user.setId(id); + user.setDelFlag(1); + return updateById(user); + } + + @Override + public LoginResponse login(LoginRequest loginRequest) { + // 根据用户名查询用户 + User user = getUserByUsername(loginRequest.getUsername()); + if (user == null) { + throw new RuntimeException("用户名或密码错误"); + } + + // 验证密码 + if (!passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) { + throw new RuntimeException("用户名或密码错误"); + } + + // 生成JWT令牌 + Map claims = new HashMap<>(); + claims.put("userId", user.getId()); + claims.put("username", user.getUsername()); + claims.put("role", user.getRole()); + String token = JwtUtils.generateToken(claims); + + // 构建登录响应 + LoginResponse loginResponse = new LoginResponse(); + loginResponse.setToken(token); + loginResponse.setUser(user); + + return loginResponse; + } +} diff --git a/tacit-admin/src/main/resources/application-dev.yml b/tacit-admin/src/main/resources/application-dev.yml new file mode 100644 index 0000000..e03d6e4 --- /dev/null +++ b/tacit-admin/src/main/resources/application-dev.yml @@ -0,0 +1,30 @@ +server: + port: 8081 + +spring: + datasource: + driver-class-name: com.oceanbase.jdbc.Driver + url: jdbc:oceanbase://localhost:2881/tacit?useUnicode=true&characterEncoding=utf-8&useSSL=false + username: root + password: password + +mybatis-plus: + configuration: + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + mapper-locations: classpath*:mapper/**/*.xml + type-aliases-package: com.tacit.admin.entity + +# Swagger Configuration +springdoc: + api-docs: + enabled: true + path: /v3/api-docs + swagger-ui: + enabled: true + path: /swagger-ui.html + +# Logging Configuration +logging: + level: + com.tacit.admin: debug diff --git a/tacit-admin/src/main/resources/bootstrap.yml b/tacit-admin/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..4e12c8e --- /dev/null +++ b/tacit-admin/src/main/resources/bootstrap.yml @@ -0,0 +1,16 @@ +spring: + application: + name: tacit-admin + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: public + config: + server-addr: localhost:8848 + namespace: public + file-extension: yml + group: DEFAULT_GROUP + refresh-enabled: true + profiles: + active: dev diff --git a/tacit-admin/target/classes/application-dev.yml b/tacit-admin/target/classes/application-dev.yml new file mode 100644 index 0000000..e03d6e4 --- /dev/null +++ b/tacit-admin/target/classes/application-dev.yml @@ -0,0 +1,30 @@ +server: + port: 8081 + +spring: + datasource: + driver-class-name: com.oceanbase.jdbc.Driver + url: jdbc:oceanbase://localhost:2881/tacit?useUnicode=true&characterEncoding=utf-8&useSSL=false + username: root + password: password + +mybatis-plus: + configuration: + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + mapper-locations: classpath*:mapper/**/*.xml + type-aliases-package: com.tacit.admin.entity + +# Swagger Configuration +springdoc: + api-docs: + enabled: true + path: /v3/api-docs + swagger-ui: + enabled: true + path: /swagger-ui.html + +# Logging Configuration +logging: + level: + com.tacit.admin: debug diff --git a/tacit-admin/target/classes/bootstrap.yml b/tacit-admin/target/classes/bootstrap.yml new file mode 100644 index 0000000..4e12c8e --- /dev/null +++ b/tacit-admin/target/classes/bootstrap.yml @@ -0,0 +1,16 @@ +spring: + application: + name: tacit-admin + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: public + config: + server-addr: localhost:8848 + namespace: public + file-extension: yml + group: DEFAULT_GROUP + refresh-enabled: true + profiles: + active: dev diff --git a/tacit-admin/target/classes/com/tacit/admin/AdminApplication.class b/tacit-admin/target/classes/com/tacit/admin/AdminApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..d73ed803fd2b8ad2b4d16945232da608e61ef63d GIT binary patch literal 930 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 c@E81zA$ZPh5w0O8}uCEygPwx1^3o~0J zU(%$%TloViG%2uZekX9{Llw{T$M7Zd92o8lig;pKCy1q#k*6;~zXB-IO(RY5DrKBj zM(fbxKG>JIJWtV-fe~DzXrDA-0oN&GD4<9n=I}El<68aznWy_Wa{!AL+ZpL$)EVz# z(wXXEria-*YEHla&k!`|sgGeClbE1<60>lSqrFqIe4lKQdX1DD@%fu^T*9y267UmN CrvMKC literal 0 HcmV?d00001 diff --git a/tacit-admin/target/classes/com/tacit/admin/controller/TestController.class b/tacit-admin/target/classes/com/tacit/admin/controller/TestController.class new file mode 100644 index 0000000000000000000000000000000000000000..61da2cd763ff617c0154577a75a16440a5da469d GIT binary patch literal 1872 zcmbVMQEwDg6h61z?$UM@S_BIsE~vQMDpv&&*;=w`0-CJQ(ApQD?#|wwUYwb`-nrAJ zF(xFMm}tZoO_cazjEV6DOf*&=;N9O*O8pbY@6N1cT1(jYF!#=#Ip=)meCNC8&%bZq zA)*GIAE5ynEK#9MLsVq6@2Xf9JP?u3FSo8r&oU}bt4P@yMuXMb;vU+==+Ij-2y~@m zbXXDXP(_tknq}q5V|dlCSn z6epf1r1GQLKp{^?lmEr-xc|Jd)3!cJ2kFTYO_b>n9cEO0fZQwmA&!hjeQDnXe{V+} z&FEad{HBil{)tw1)SNLyhQh-P%Qsg2L$u2GTSF(Q;J+Pu$OQbaND z2+wM>%EgugZzNW)C?nf-Mul13mW=i`RU|JZVN03?f$)sRo7xk>qA<#xdmjsSNda#v z$K4~y2gJ~lQxq7l_T6hn!}I8cu!%t`GkrTw=kzsi9Ub) z!-rYMA2B+&+8F7~{iOKqM{6FLIx6L?;TsY zBps$gigO-TuAtG$Uuo~uA9P@Vc7h#E!476-Juo<+K9^($`^rw(R~_uB9PDX&4KO8F f)tNNY*VFZE>R3V5Z{U1xE6X}f1MtmW{xkmoHd-zY literal 0 HcmV?d00001 diff --git a/tacit-admin/target/classes/com/tacit/admin/controller/UserController.class b/tacit-admin/target/classes/com/tacit/admin/controller/UserController.class new file mode 100644 index 0000000000000000000000000000000000000000..027bd73d7063a3de5cd2a46eb0926a7b68f904d1 GIT binary patch literal 4220 zcmb_f>vK~@6hE7`1k#HwEuuhC-lQ$EDo;s4`w%RqwDyHORBn=Odg12Ya38JFanx~e z+8G5uC=P=&!iWxE1RR*5&{4npcaQ}C3CFX0Z<3odNmGUo&EDNTyZbx8-#KTu|NQsp zF97IL#}mvt$cVjU(>7i=FVP~WS^ zSODr^r35P=K%gnCXzaM1h%jSR#&Zd@g!QPbj?0F^`|e=9HK`zw&IQg?WCzb~Q1!T? z5f}+BSjNy-D@9|LPQ%=2m|d`$X@%N7mdIN|*Qjec%*-T`!LYSe3wK{JqQo`XvJJ%i z`LjQv%Y(!TgoT(LM4`iorai3a)SQ;%ag39x-Bg!Nk0d4?Ofo~riMD>zP9$U_ML^1a ze=9TdAbWK_{pSxDIWcBN4JFBCC$Qc-`Q*}r^yMqZPK-XDznPu=hQQvkF)_^|G@@uR z&)IZHH?09VnZ)4XqD;xEjg&2;t~#5G18+#?0cJ6yC<1Fg%~i5TkEKE@fWRgR8emx? zY=Lcl*h*ka4e2033v4Ga_H?Q)qE-Nc(BX%60^18vs>PZFov>4=7L76!v!E*&F3tL- z1g<2AcX{HGQ-Dzkx|19PR=P`_z$4I6!&)~;F}mW+>Q+@=#L$V3p!eWv{$tRbuGzTD zp~kipm4+45s+dL`24)V=ofap!GqYc0ewuZH9^=Lt%-);N&j0*)?sxBOdgjL`H*OKw zQ*#a+W!@B^Dk39ruzU_?L5Ro61)ao=n?WILL|=v&pduFAoI-&r`8nL+*C4GcWH_3Vsttxb8dT5{V{aW z8>(R)E5H&|Kl#*)7l*kzbNylF+uQwpuFRqi64>YrJ3&x9`Q*-TnXfJq2v9|v(CMd2 z>@x!U%1T(a3d+`G?otbJt@&&y+$xUG4SC5VXE^8u5rS#0T^!%e-dNw)T%$2*j5kGbO z;@pl)FbF45&P6bt3cyJ??T1qYRuxLBd#nU!-~+6?;=&r7Ah0HQ%1x^xba5fw}NPkf`H@NHOl6CP5r@0<8-j;(U+jwh?7V z6#jT@<*pZ=$h)|xmEN!AvJ_u;R$)07&GJv*@=L`nS@5HQ+9Jh*i#*LNId0NXnLm!| zHGKONo@KXhWWW0wtGPpX_S#(f;=RnJU(=U=flc_dsl$I8up0394Yq#l@pd`3er)l1 zjlYf9qu3H`8#*3<^apo)vagMTlW z@FJ`=VI8bTySCFnOxOS~p{1Xn@-lw#Gl$WX<7@7?4;wol!e$=~?3{xgcX7-)K7cLY zCXLW0AZ&E(T?n`mUV&Y>Vy?~CRxq5$GJ1cz~xH*u5`=8U|BUj$2e zPoA*eQe=G`+1nmuM+(UD%pJvD%Q5#(1?D=@dS?aZ-i0t?;>-=ealzam4CV2?SBh_# z;~Vh^I$8!_PT4Wsk1N~iDm&(bLv3^Lei?ChBZNI3h^={XTLmx>rSZW90gk7XfqgR%$pRZ^r%agUU_bsJ-fGR7 literal 0 HcmV?d00001 diff --git a/tacit-admin/target/classes/com/tacit/admin/entity/User.class b/tacit-admin/target/classes/com/tacit/admin/entity/User.class new file mode 100644 index 0000000000000000000000000000000000000000..a3b98df361bcacb9ccf2f512ad23023eb9c7e721 GIT binary patch literal 6753 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 Ir$3VUAN|V*3IG5A literal 0 HcmV?d00001 diff --git a/tacit-admin/target/classes/com/tacit/admin/entity/dto/LoginRequest.class b/tacit-admin/target/classes/com/tacit/admin/entity/dto/LoginRequest.class new file mode 100644 index 0000000000000000000000000000000000000000..1916b8a8cf2d04b09d8705c95a0eb89afc4598c5 GIT binary patch literal 2429 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 S;d2)sai@Qh@8M&7iqyY04*%!? literal 0 HcmV?d00001 diff --git a/tacit-admin/target/classes/com/tacit/admin/mapper/UserMapper.class b/tacit-admin/target/classes/com/tacit/admin/mapper/UserMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..ae164c4ca7a64fd170a249caccdae3a246f52188 GIT binary patch literal 381 zcmb7AyH3ME5S$ATlK_I2l7^D~03)POP#GZ+LBDoZ;UeFib$3R}uTk&;d=z5iC<>yZ z*oU+`GduhB{qYH4hO+`AgR3Su%G}sYyl~c&>qM$xx>_(K^=oDewdB{B-F6 literal 0 HcmV?d00001 diff --git a/tacit-admin/target/classes/com/tacit/admin/service/UserService.class b/tacit-admin/target/classes/com/tacit/admin/service/UserService.class new file mode 100644 index 0000000000000000000000000000000000000000..fdc0f6d02df8896b224d2a7d5a449cb2868e9fbc GIT binary patch literal 790 zcmb7C%T59@6um`p1bp!DxifJC8yn*iV}c74l1WUQuyI#ru9K3P7N;E|zvjXZ@S}`v z2Z3lnUG#nKx#!-V_m9^%0O-M~2PFdMp-ib}A=i{eDHl{>HsK-Cf!PGb-h)j7^%0ve znlKU5`{4*f?ZFm-D;qv!GUbuXX*wM;&DA)`6~z~gLUFlZ?9b>3G-A}2|8{C;!BQk} z+4dJ10-bRYcRT)fbtp8~Qzx!Vpf=~}M+E3HgD?LW-%AoJLJ?@TJB5T?^Mv|bX_KN7 z@K`XNXHMdG!1Y>zHzvLsX2>+!6a>!NYw?{&gU`p&9AAwv!LQ)f&+-huoXD690)v&* zk(SiApa*=;(G<0^t4l~VmO`P`AILll@s1~^0d-qOP?+Mv{uY6!rC%$2(RDTc(-Xfn z(pZhah8bn!UC-FjgKe<3tU%SRHP|t)yXLh7d**&0>h8WVTbs~w=L0x&m?JkI&mvAf E0iC4n!vFvP literal 0 HcmV?d00001 diff --git a/tacit-admin/target/classes/com/tacit/admin/service/impl/UserServiceImpl.class b/tacit-admin/target/classes/com/tacit/admin/service/impl/UserServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..0678d41d3f6cece0588c7c3b1872b792a3722675 GIT binary patch literal 4374 zcmbtX`*Ryt75=XLkSwqBP|`H6nBXdM_c$G7>4ieuH^MYNr2IKA9wHl&N<)t z&bilr|M#E&0$>>LB#{v4Go7;T8>Z#!Mxkukx+mRv%apoRu9WmizDbuE8EH{FGb@94E<`tjxK z3E4Vm*_J;f(3I_-5@ZGF(f&M}y7& zti{;&tzw7NHB2JSZ!Ah*k$rGMU2U=<(4WoE8S{o-GVG#0;VYhU-K&5l31)<9SuTmr zJE_5RT&bImU9f@_=ohMz9G_6vX*bAT8e6e3g$D&vD%r{pHS~~%?bsoZkQbP@xLe0& z=A`N8Vva^5-K&RE_y}N5R4ndbm_lDrb3RvJv@J-zw>O%k#OkZNe(rhF^7q z(pfp-Zr-PGkXB4r8ooTMT9(rNh=#)$$ zRAA(P7h62ubm*+2L|{jV8Q(TyvdeX!NuwV}H5|k7z#D%=mPT2HsujnfPyeKs#wly_#6y@7Ek(W5G(Xs_58`4wYwvbnoPD<7ih19Ua#%6jI z?qD*PhD0$5_NP4!YCg6mK-Vw}YpGDeKEJu9=6Iu56rI;Y+5h zs6L*8Be3`Pi3CF6r!gex#&--2MyoD_i3ysZRxTYPC4f#<+&^3pk{vA-V(*0#2W(H zCH(Mg7!`R;XQ4LJxDM9Ib^J)fk5%qdtdqc+8hlg3Pw_Jbz`=X#AFL%UsHLb(1n#9y z!ir*2%#vZ1ne6ppuc^n$49T9ryKDYHo-DFj$Z2gZehaHS{w1~afNYuI1hSf2_ zhIyX(j!tjs38(6sa@11iyUmK3iKV0VKBW+Ww;J!EcRy{f-i6AmPR)%1SMKD7a;rXb zI5=6x++}FlwGYI;bfx3-M4(5J<*<|W-^9NbB>3Z5iqCEQf25W7H17%38di*{IXo8g zx}Gz1-UaV1J-4vokHL}*uL=Ozj7_{M08ZfBeY|tm!Tr3d#aC#&iT9pGY(0Go4~+L^ zx_WP7+k4oW+1d0icJX<-=TGSFyN%ui-b4R8oZSRvA%kXKAEL|K`Md)UG8koTM~E0% zBR0Q$g7$a<#Upr>Aw9-f%BOH-KSy}5#iw{};M2f==hJQ?@1ajwTJCP(Qx1c54;uM& z07LZcaeAA;rvu+EF}4KXcSn5Xd+RhQ%7$;^P(WAjZG0wym0azkUpjN}FuK^t6jxh9 zw1}$|*;EXP7(;4gKSuqpKoYp$dXYq98nRM#TF7Z!`VgrZiKVNnZm0{jbZrVuD#6Ua3{8qhXt0$gFDuszH!(Nf_cwGb zqI5cA-$LcD)UU@cRocFE8w*W%BmQ*}&(Y5pj_%ETg%e)9jjK(V-ka&-%PX~)SNZZ< z^ztsgGtj(<>s<7MftJpe&gPr=VP^|x{G_u*WxP4a!2k;sIKpCnlA+~UamUe%6U^=; z4v_sLnC9;(6mS}IWY5Dh+~c3c^E_E8WekNO)hW1)pW`hC+{RfddkLb9;urWOgP%hu we#IJVCdvl9jbF3YYc&?m`VD9OmZ)t={7W5b`nPc)O!Ds|f&Zc2cWwax0X@N&C;$Ke literal 0 HcmV?d00001 diff --git a/tacit-app-api/pom.xml b/tacit-app-api/pom.xml new file mode 100644 index 0000000..3f4b5b2 --- /dev/null +++ b/tacit-app-api/pom.xml @@ -0,0 +1,101 @@ + + + + tacit-parent + com.tacit + 1.0.0-SNAPSHOT + + 4.0.0 + + tacit-app-api + Tacit App API + App API Service for Tacit Microservices + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-security + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.baomidou + mybatis-plus-boot-starter + + + + + com.oceanbase + oceanbase-client + 2.4.0 + + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.3.0 + + + + + com.tacit + tacit-common + ${project.parent.version} + + + + + org.projectlombok + lombok + provided + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + false + + + + + \ No newline at end of file diff --git a/tacit-app-api/src/main/java/com/tacit/app/AppApiApplication.java b/tacit-app-api/src/main/java/com/tacit/app/AppApiApplication.java new file mode 100644 index 0000000..866915e --- /dev/null +++ b/tacit-app-api/src/main/java/com/tacit/app/AppApiApplication.java @@ -0,0 +1,17 @@ +package com.tacit.app; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; + +@SpringBootApplication +@EnableDiscoveryClient +@EnableFeignClients +@MapperScan("com.tacit.app.mapper") +public class AppApiApplication { + public static void main(String[] args) { + SpringApplication.run(AppApiApplication.class, args); + } +} diff --git a/tacit-app-api/src/main/java/com/tacit/app/config/SwaggerConfig.java b/tacit-app-api/src/main/java/com/tacit/app/config/SwaggerConfig.java new file mode 100644 index 0000000..4b4bbfc --- /dev/null +++ b/tacit-app-api/src/main/java/com/tacit/app/config/SwaggerConfig.java @@ -0,0 +1,23 @@ +package com.tacit.app.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Swagger配置类 + * 使用SpringDoc OpenAPI配置API文档 + */ +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI appApiOpenAPI() { + return new OpenAPI() + .info(new Info() + .title("Tacit App API") + .description("App接口服务API文档") + .version("1.0.0")); + } +} diff --git a/tacit-app-api/src/main/java/com/tacit/app/controller/AuthController.java b/tacit-app-api/src/main/java/com/tacit/app/controller/AuthController.java new file mode 100644 index 0000000..7a24f29 --- /dev/null +++ b/tacit-app-api/src/main/java/com/tacit/app/controller/AuthController.java @@ -0,0 +1,49 @@ +package com.tacit.app.controller; + +import com.tacit.app.entity.User; +import com.tacit.app.entity.dto.LoginRequest; +import com.tacit.app.entity.dto.LoginResponse; +import com.tacit.app.service.UserService; +import com.tacit.common.entity.ResponseResult; +import com.tacit.common.feign.AdminFeignClient; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/auth") +@Tag(name = "认证管理", description = "用户认证相关接口") +public class AuthController { + + @Autowired + private UserService userService; + + @Autowired + private AdminFeignClient adminFeignClient; + + @Operation(summary = "用户登录", description = "用户登录接口") + @PostMapping("/login") + public ResponseResult login(@RequestBody LoginRequest loginRequest) { + LoginResponse loginResponse = userService.login(loginRequest); + return ResponseResult.success(loginResponse); + } + + @Operation(summary = "用户注册", description = "用户注册接口") + @PostMapping("/register") + public ResponseResult register(@RequestBody User user) { + boolean result = userService.register(user); + return ResponseResult.success(result); + } + + @Operation(summary = "测试Feign调用", description = "测试通过Feign调用管理台服务") + @GetMapping("/test-feign/{userId}") + public ResponseResult testFeign(@PathVariable Long userId) { + return adminFeignClient.getUserById(userId); + } +} diff --git a/tacit-app-api/src/main/java/com/tacit/app/controller/UserController.java b/tacit-app-api/src/main/java/com/tacit/app/controller/UserController.java new file mode 100644 index 0000000..ece8936 --- /dev/null +++ b/tacit-app-api/src/main/java/com/tacit/app/controller/UserController.java @@ -0,0 +1,35 @@ +package com.tacit.app.controller; + +import com.tacit.app.entity.User; +import com.tacit.app.service.UserService; +import com.tacit.common.entity.ResponseResult; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/user") +@Tag(name = "用户管理", description = "用户相关接口") +public class UserController { + + @Autowired + private UserService userService; + + @Operation(summary = "获取用户信息", description = "根据用户ID获取用户信息") + @GetMapping("/info/{userId}") + @PreAuthorize("hasRole('USER')") + public ResponseResult getUserInfo(@PathVariable Long userId) { + User user = userService.getUserInfo(userId); + return ResponseResult.success(user); + } + + @Operation(summary = "更新用户信息", description = "更新用户个人信息") + @PutMapping("/update") + @PreAuthorize("hasRole('USER')") + public ResponseResult updateUserInfo(@RequestBody User user) { + boolean result = userService.updateUserInfo(user); + return ResponseResult.success(result); + } +} diff --git a/tacit-app-api/src/main/java/com/tacit/app/entity/User.java b/tacit-app-api/src/main/java/com/tacit/app/entity/User.java new file mode 100644 index 0000000..a470433 --- /dev/null +++ b/tacit-app-api/src/main/java/com/tacit/app/entity/User.java @@ -0,0 +1,28 @@ +package com.tacit.app.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Data +@TableName("t_user") +public class User implements Serializable { + private static final long serialVersionUID = 1L; + + @TableId(type = IdType.AUTO) + private Long id; + private String username; + private String password; + private String nickname; + private String email; + private String phone; + private Integer status; + private String role; + private LocalDateTime createTime; + private LocalDateTime updateTime; + private Integer delFlag; +} diff --git a/tacit-app-api/src/main/java/com/tacit/app/entity/dto/LoginRequest.java b/tacit-app-api/src/main/java/com/tacit/app/entity/dto/LoginRequest.java new file mode 100644 index 0000000..b4d725c --- /dev/null +++ b/tacit-app-api/src/main/java/com/tacit/app/entity/dto/LoginRequest.java @@ -0,0 +1,9 @@ +package com.tacit.app.entity.dto; + +import lombok.Data; + +@Data +public class LoginRequest { + private String username; + private String password; +} diff --git a/tacit-app-api/src/main/java/com/tacit/app/entity/dto/LoginResponse.java b/tacit-app-api/src/main/java/com/tacit/app/entity/dto/LoginResponse.java new file mode 100644 index 0000000..48f6052 --- /dev/null +++ b/tacit-app-api/src/main/java/com/tacit/app/entity/dto/LoginResponse.java @@ -0,0 +1,11 @@ +package com.tacit.app.entity.dto; + +import com.tacit.app.entity.User; +import lombok.Data; + +@Data +public class LoginResponse { + private User user; + private String token; + private Long expireTime; +} diff --git a/tacit-app-api/src/main/java/com/tacit/app/mapper/UserMapper.java b/tacit-app-api/src/main/java/com/tacit/app/mapper/UserMapper.java new file mode 100644 index 0000000..5424eec --- /dev/null +++ b/tacit-app-api/src/main/java/com/tacit/app/mapper/UserMapper.java @@ -0,0 +1,9 @@ +package com.tacit.app.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.tacit.app.entity.User; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface UserMapper extends BaseMapper { +} diff --git a/tacit-app-api/src/main/java/com/tacit/app/service/UserService.java b/tacit-app-api/src/main/java/com/tacit/app/service/UserService.java new file mode 100644 index 0000000..348f9a6 --- /dev/null +++ b/tacit-app-api/src/main/java/com/tacit/app/service/UserService.java @@ -0,0 +1,36 @@ +package com.tacit.app.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.tacit.app.entity.User; +import com.tacit.app.entity.dto.LoginRequest; +import com.tacit.app.entity.dto.LoginResponse; + +public interface UserService extends IService { + /** + * 用户登录 + * @param loginRequest 登录请求参数 + * @return 登录响应结果 + */ + LoginResponse login(LoginRequest loginRequest); + + /** + * 用户注册 + * @param user 用户信息 + * @return 注册结果 + */ + boolean register(User user); + + /** + * 根据用户ID获取用户信息 + * @param userId 用户ID + * @return 用户信息 + */ + User getUserInfo(Long userId); + + /** + * 更新用户信息 + * @param user 用户信息 + * @return 更新结果 + */ + boolean updateUserInfo(User user); +} diff --git a/tacit-app-api/src/main/java/com/tacit/app/service/impl/UserServiceImpl.java b/tacit-app-api/src/main/java/com/tacit/app/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..f3b7e9d --- /dev/null +++ b/tacit-app-api/src/main/java/com/tacit/app/service/impl/UserServiceImpl.java @@ -0,0 +1,115 @@ +package com.tacit.app.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.tacit.app.entity.User; +import com.tacit.app.entity.dto.LoginRequest; +import com.tacit.app.entity.dto.LoginResponse; +import com.tacit.app.mapper.UserMapper; +import com.tacit.app.service.UserService; +import com.tacit.common.constant.CommonConstant; +import com.tacit.common.exception.BusinessException; +import com.tacit.common.utils.JwtUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +@Service +public class UserServiceImpl extends ServiceImpl implements UserService { + + @Autowired + private UserMapper userMapper; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Override + public LoginResponse login(LoginRequest loginRequest) { + // 根据用户名查询用户 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("username", loginRequest.getUsername()) + .eq("del_flag", 0); + User user = userMapper.selectOne(queryWrapper); + + // 检查用户是否存在 + if (user == null) { + throw new BusinessException("用户名或密码错误"); + } + + // 检查用户状态 + if (user.getStatus() == 0) { + throw new BusinessException("用户已禁用"); + } + + // 验证密码 + if (!passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) { + throw new BusinessException("用户名或密码错误"); + } + + // 生成JWT令牌 + Map claims = new HashMap<>(); + claims.put("userId", user.getId()); + claims.put("username", user.getUsername()); + claims.put("role", user.getRole()); + String token = JwtUtils.generateToken(claims); + + // 构造登录响应 + LoginResponse loginResponse = new LoginResponse(); + loginResponse.setUser(user); + loginResponse.setToken(token); + loginResponse.setExpireTime(System.currentTimeMillis() + CommonConstant.JWT_EXPIRE_TIME); + + return loginResponse; + } + + @Override + public boolean register(User user) { + // 检查用户名是否已存在 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("username", user.getUsername()); + User existingUser = userMapper.selectOne(queryWrapper); + if (existingUser != null) { + throw new BusinessException("用户名已存在"); + } + + // 加密密码 + user.setPassword(passwordEncoder.encode(user.getPassword())); + // 设置默认角色 + user.setRole(CommonConstant.ROLE_USER); + // 设置默认状态 + user.setStatus(1); + // 设置删除标记 + user.setDelFlag(0); + + // 保存用户 + return save(user); + } + + @Override + public User getUserInfo(Long userId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("id", userId) + .eq("del_flag", 0); + return userMapper.selectOne(queryWrapper); + } + + @Override + public boolean updateUserInfo(User user) { + // 只更新允许修改的字段 + User existingUser = getUserInfo(user.getId()); + if (existingUser == null) { + throw new BusinessException("用户不存在"); + } + + // 更新用户信息 + existingUser.setNickname(user.getNickname()); + existingUser.setEmail(user.getEmail()); + existingUser.setPhone(user.getPhone()); + + return updateById(existingUser); + } +} diff --git a/tacit-app-api/src/main/resources/application-dev.yml b/tacit-app-api/src/main/resources/application-dev.yml new file mode 100644 index 0000000..543bc32 --- /dev/null +++ b/tacit-app-api/src/main/resources/application-dev.yml @@ -0,0 +1,30 @@ +server: + port: 8082 + +spring: + datasource: + driver-class-name: com.oceanbase.jdbc.Driver + url: jdbc:oceanbase://localhost:2881/tacit?useUnicode=true&characterEncoding=utf-8&useSSL=false + username: root + password: password + +mybatis-plus: + configuration: + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + mapper-locations: classpath*:mapper/**/*.xml + type-aliases-package: com.tacit.app.entity + +# Swagger Configuration +springdoc: + api-docs: + enabled: true + path: /v3/api-docs + swagger-ui: + enabled: true + path: /swagger-ui.html + +# Logging Configuration +logging: + level: + com.tacit.app: debug diff --git a/tacit-app-api/src/main/resources/bootstrap.yml b/tacit-app-api/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..ad0ac67 --- /dev/null +++ b/tacit-app-api/src/main/resources/bootstrap.yml @@ -0,0 +1,16 @@ +spring: + application: + name: tacit-app-api + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: public + config: + server-addr: localhost:8848 + namespace: public + file-extension: yml + group: DEFAULT_GROUP + refresh-enabled: true + profiles: + active: dev diff --git a/tacit-app-api/src/test/java/com/tacit/app/service/impl/UserServiceImplTest.java b/tacit-app-api/src/test/java/com/tacit/app/service/impl/UserServiceImplTest.java new file mode 100644 index 0000000..7fdc4b2 --- /dev/null +++ b/tacit-app-api/src/test/java/com/tacit/app/service/impl/UserServiceImplTest.java @@ -0,0 +1,196 @@ +package com.tacit.app.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.tacit.app.entity.User; +import com.tacit.app.entity.dto.LoginRequest; +import com.tacit.app.entity.dto.LoginResponse; +import com.tacit.app.mapper.UserMapper; +import com.tacit.common.constant.CommonConstant; +import com.tacit.common.exception.BusinessException; +import com.tacit.common.utils.JwtUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.crypto.password.PasswordEncoder; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +/** + * UserServiceImpl单元测试 + */ +@ExtendWith(MockitoExtension.class) +public class UserServiceImplTest { + + @InjectMocks + private UserServiceImpl userService; + + @Mock + private UserMapper userMapper; + + @Mock + private PasswordEncoder passwordEncoder; + + private User testUser; + private LoginRequest testLoginRequest; + + @BeforeEach + public void setUp() { + // 初始化测试用户 + testUser = new User(); + testUser.setId(1L); + testUser.setUsername("testuser"); + testUser.setPassword("encryptedPassword"); + testUser.setRole(CommonConstant.ROLE_USER); + testUser.setStatus(1); + testUser.setDelFlag(0); + + // 初始化登录请求 + testLoginRequest = new LoginRequest(); + testLoginRequest.setUsername("testuser"); + testLoginRequest.setPassword("password123"); + } + + @Test + public void testLoginSuccess() { + // 模拟查询用户 + when(userMapper.selectOne(any(QueryWrapper.class))).thenReturn(testUser); + // 模拟密码验证 + when(passwordEncoder.matches(testLoginRequest.getPassword(), testUser.getPassword())).thenReturn(true); + + // 执行登录 + LoginResponse loginResponse = userService.login(testLoginRequest); + + // 验证结果 + assertNotNull(loginResponse); + assertNotNull(loginResponse.getToken()); + assertEquals(testUser, loginResponse.getUser()); + verify(userMapper, times(1)).selectOne(any(QueryWrapper.class)); + verify(passwordEncoder, times(1)).matches(anyString(), anyString()); + } + + @Test + public void testLoginUserNotFound() { + // 模拟用户不存在 + when(userMapper.selectOne(any(QueryWrapper.class))).thenReturn(null); + + // 验证抛出异常 + BusinessException exception = assertThrows(BusinessException.class, () -> { + userService.login(testLoginRequest); + }); + assertEquals("用户名或密码错误", exception.getMessage()); + verify(userMapper, times(1)).selectOne(any(QueryWrapper.class)); + } + + @Test + public void testLoginUserDisabled() { + // 设置用户为禁用状态 + testUser.setStatus(0); + when(userMapper.selectOne(any(QueryWrapper.class))).thenReturn(testUser); + + // 验证抛出异常 + BusinessException exception = assertThrows(BusinessException.class, () -> { + userService.login(testLoginRequest); + }); + assertEquals("用户已禁用", exception.getMessage()); + } + + @Test + public void testLoginPasswordError() { + // 模拟密码验证失败 + when(userMapper.selectOne(any(QueryWrapper.class))).thenReturn(testUser); + when(passwordEncoder.matches(testLoginRequest.getPassword(), testUser.getPassword())).thenReturn(false); + + // 验证抛出异常 + BusinessException exception = assertThrows(BusinessException.class, () -> { + userService.login(testLoginRequest); + }); + assertEquals("用户名或密码错误", exception.getMessage()); + } + + @Test + public void testRegisterSuccess() { + // 模拟用户名不存在 + when(userMapper.selectOne(any(QueryWrapper.class))).thenReturn(null); + // 模拟密码加密 + when(passwordEncoder.encode(testUser.getPassword())).thenReturn("encryptedPassword"); + // 模拟保存成功 + when(userService.save(testUser)).thenReturn(true); + + // 执行注册 + boolean result = userService.register(testUser); + + // 验证结果 + assertTrue(result); + assertEquals(CommonConstant.ROLE_USER, testUser.getRole()); + assertEquals(1, testUser.getStatus()); + assertEquals(0, testUser.getDelFlag()); + verify(userMapper, times(1)).selectOne(any(QueryWrapper.class)); + verify(passwordEncoder, times(1)).encode(anyString()); + verify(userService, times(1)).save(testUser); + } + + @Test + public void testRegisterUsernameExists() { + // 模拟用户名已存在 + when(userMapper.selectOne(any(QueryWrapper.class))).thenReturn(testUser); + + // 验证抛出异常 + BusinessException exception = assertThrows(BusinessException.class, () -> { + userService.register(testUser); + }); + assertEquals("用户名已存在", exception.getMessage()); + } + + @Test + public void testGetUserInfo() { + // 模拟查询用户 + when(userMapper.selectOne(any(QueryWrapper.class))).thenReturn(testUser); + + // 执行查询 + User user = userService.getUserInfo(1L); + + // 验证结果 + assertNotNull(user); + assertEquals(testUser.getId(), user.getId()); + verify(userMapper, times(1)).selectOne(any(QueryWrapper.class)); + } + + @Test + public void testUpdateUserInfo() { + // 模拟查询用户 + when(userMapper.selectOne(any(QueryWrapper.class))).thenReturn(testUser); + // 模拟更新成功 + when(userService.updateById(testUser)).thenReturn(true); + + // 修改用户信息 + testUser.setNickname("新昵称"); + testUser.setEmail("new@example.com"); + + // 执行更新 + boolean result = userService.updateUserInfo(testUser); + + // 验证结果 + assertTrue(result); + assertEquals("新昵称", testUser.getNickname()); + assertEquals("new@example.com", testUser.getEmail()); + verify(userMapper, times(1)).selectOne(any(QueryWrapper.class)); + verify(userService, times(1)).updateById(testUser); + } + + @Test + public void testUpdateUserInfoUserNotFound() { + // 模拟用户不存在 + when(userMapper.selectOne(any(QueryWrapper.class))).thenReturn(null); + + // 验证抛出异常 + BusinessException exception = assertThrows(BusinessException.class, () -> { + userService.updateUserInfo(testUser); + }); + assertEquals("用户不存在", exception.getMessage()); + } +} diff --git a/tacit-app-api/target/classes/application-dev.yml b/tacit-app-api/target/classes/application-dev.yml new file mode 100644 index 0000000..543bc32 --- /dev/null +++ b/tacit-app-api/target/classes/application-dev.yml @@ -0,0 +1,30 @@ +server: + port: 8082 + +spring: + datasource: + driver-class-name: com.oceanbase.jdbc.Driver + url: jdbc:oceanbase://localhost:2881/tacit?useUnicode=true&characterEncoding=utf-8&useSSL=false + username: root + password: password + +mybatis-plus: + configuration: + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + mapper-locations: classpath*:mapper/**/*.xml + type-aliases-package: com.tacit.app.entity + +# Swagger Configuration +springdoc: + api-docs: + enabled: true + path: /v3/api-docs + swagger-ui: + enabled: true + path: /swagger-ui.html + +# Logging Configuration +logging: + level: + com.tacit.app: debug diff --git a/tacit-app-api/target/classes/bootstrap.yml b/tacit-app-api/target/classes/bootstrap.yml new file mode 100644 index 0000000..ad0ac67 --- /dev/null +++ b/tacit-app-api/target/classes/bootstrap.yml @@ -0,0 +1,16 @@ +spring: + application: + name: tacit-app-api + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: public + config: + server-addr: localhost:8848 + namespace: public + file-extension: yml + group: DEFAULT_GROUP + refresh-enabled: true + profiles: + active: dev diff --git a/tacit-app-api/target/classes/com/tacit/app/AppApiApplication.class b/tacit-app-api/target/classes/com/tacit/app/AppApiApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..0ad3508e647ea69d7fd9ca2522f066ad59351d17 GIT binary patch literal 927 zcmah|O>fgc5Ph2_bs7Qbd1jT4 z7gnV5qp=@&XpH02(z#YiAF+jI2z!(3M#hQA^WpiLXX-rI@OZKbIWpSGPmYg`UZ}XR zB247kC&=x?Lx$?u%q7F_L}~e|NJDAg`do(g#6%*Q39G!la#r1Yl{0iF{~6u`r$W&X zo!+LNXA~DUCaAp)VTQ)k6gHA)%6HQFi`#+ch2h{$p`A+QOy$bYr?qh<_k?@0k(QEh zQ8+{Vl)T6l{ik#jYy43EryBvH#1wNLB}!_?=PHlPQrb)YLVJ>)|F*|voIsxb1H@!f zFQkg~s`|{oUsdK*+AmAf%aA6@SDVAdpAq>>k!8|OBcUm|wiHQ0RPEEf49cY(P|qLMZK?K^-PxPd9eQiz*KAjy#3 z6@9@w!m;^QYgbw!L-M|IRQP})Rw_4BNHY|a%Y!{(+tTO#JKPlk@47p(6YwohI<=>p z22vVynqm2$t;%V;e3OC!1FbbX%rVRp^6>rV;|~YJPw$T3?i0!IdKK%dfk@vw?#{vm8-j!=ro0|bUxmUhc8vYuu?xAx=J@f zv^7tSMbWX+1kxE6p7oqib!Afpil}RjTO3Z6@*SC4#>AP$DqAT}Y|CJssl&@*b zKdtN|%RmO#D4L@NT*nQ{7)mG;h(7!Rv8Yx&LhAV;&K$v@#mr>}n90o!kk8Ez zusFcd7iyk@23{m+&^ym!7J1B3p2re$DA3*sS#eCZO1)aj&FK8CD6Zl6ZUy)SWZwMm literal 0 HcmV?d00001 diff --git a/tacit-app-api/target/classes/com/tacit/app/controller/AuthController.class b/tacit-app-api/target/classes/com/tacit/app/controller/AuthController.class new file mode 100644 index 0000000000000000000000000000000000000000..aefb23d41b5ea5e9f6cf6ef5a4717ef473d9ca66 GIT binary patch literal 2980 zcmb_e>vI!T6#w0jHf;s^QQ;|rtC@CoXmL)B5g`+uaZ|AgbYyGyc(Y1(1@kloz7=kYt|{LZ=euYZ31 z13*9CZb1NBLI_6CfJTPaOJZK|tk5%jGi7MM|@|$me;=&~4MmW~Isd3-;V#X~_^SSkj!3=De1Y4Eqz6iu6r=;8SIxco+|1 zTL`TYv|&5Lz6~`P8nQ-4(-|&AtL#hN*6c-|whf+8BIEMig0$>-Y(qIKZ|Ihcw_pbz z4xuB0N3fIOKp71E%NaWNJd8_Mf!>8IB`;(ZQYmR!4874rb#5!%*R$i~;?W3pqm!Y@ zlo`#krOB{ooj6q*(-hnx!SPvR~H?Ti6;^nBio?Ff-iBo0^`PHAPM? z80I@XNm;V^tVr30xyVIHLGjyI&`g<*GXw{XG?98+Leu3)A(xcqq#*MQ+Y?4gWT%Cx zskOHmwC6PP=g(Z--XRAM#OJz%Vu(1s;xz)*W5Z5xJBDGar|1%$VI2vCndjIXwdNK8G7=eeQk1`rdXa=_yL?%PCYW^rUgypNQ#YgkfbeKW1)Wf-) zFc&Fjt6$z)`Q+Z}SHBg1zd_7QODkn+dBq?qw*TYG=RXv$f5&jVCaV@?k|#Ai?Q_Y; z49gxSA`tz^XM@w1G<>-?(z+_CJ0=uGX$Vwsc=O)8x(^uXMe=rbom#ka#Fy84`6oNU z0`)1wZl8vtwl^CcYZQ{{%|Fy|(lr5s8i%ckl1_zh3+E zW8at6+c#IgxKg~nwDRq>;-@zmj%}J^=cJEeJGhGWusf|UyV1VKaAMQe#)Lh0f3#5y zEfYq;Ovxcl4Zilu&kn!NE63#f^3G#{+f#T*hNHkVVOJ z%!jPKgsdGyIOia1!izXxLiSQMvX>RIgb&$pHL?-(c*xF>8I_PQA=^(0I~YJYS8mR^ zgQmk3E1?Nv6k~+O&CZ2t4C4yJ1SUzs&CXOA!!-E|;1%cjs-vii@EWaWO5zvsI?23Y K;Z0@et$zU-s(=ju literal 0 HcmV?d00001 diff --git a/tacit-app-api/target/classes/com/tacit/app/controller/UserController.class b/tacit-app-api/target/classes/com/tacit/app/controller/UserController.class new file mode 100644 index 0000000000000000000000000000000000000000..37e41a48f09ae1768be8dda79b98c7ffcb8fa7bd GIT binary patch literal 2377 zcmb_dOK)366#gbJ=V6*OhLX@yl9o1h`WT=ON|OeXG%W$AiJT@#VKu&DMF|>`^j_r*xG{+N*9cV{K1f5ZIA<8gh zxq0ph%l5b^6u9L&o^o?JsrZ7HYO-=<=@u8*C#%sUT_-z|*yC0@p6#s%UPn6dB3_E%P!untkKtt1 z9ew3phnIy@QWsoDOWOS$r6;0&%aU3%jE4P{vRC7=k)0sgSE4wKBMb-pLPmJ98p+Xk z1K^3JP8`K+5e!5zh+_-~t5Hw5ZcYk^P_BxcFK3q-4#k%ep}dqOMi}~2KBXfs7j12) zb8_5qTu*p5#maCg>8dQ(1!X(gWhL@*%~hZ9G?~_XSy-N{*14!C@NwUB*K8#-WVU6} z%}~X=leQyo`1!O{HwBqz=uNto$Sn$Gn{)ZH#apq--`+w!lUU^7ayae(Ds#i66S)m7 zr!z$SP)-6PgxvgNINTuj3?1`!))AhsD58)4Gm_B;KE^1p)o)lAsDRznMEg{rn6&s2F_uqB42|HJ5Lh_Jg&Y^c&(y!W_x_fc)p`)V#Xe}1~P zwabU+FYlZmDsP0Bh7`kv=gw_Pe(Fo@O}LqL0@^w6`pS~mY%>gEX4qBVi?e2gJ!|aL zD;rH?uc}Vg>rbrussXAHb9t%pIq(G!p!MS#2(RI$&PvYfj)xKAJ`fw z$(L=BCQjir+3X_wXKp&bJ2!kPru?oak0L(ya#~9wNKzwg6#Bl?0 m0+SVpSL;BW3c|Y#?rgDm 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 literal 0 HcmV?d00001 diff --git a/tacit-app-api/target/classes/com/tacit/app/entity/dto/LoginResponse.class b/tacit-app-api/target/classes/com/tacit/app/entity/dto/LoginResponse.class new file mode 100644 index 0000000000000000000000000000000000000000..a12934426e59aae6e79c354ef23ecdc31a027b20 GIT binary patch literal 2813 zcma)8U2_yg6g|B=U)fAZ2!ycmVbBGWY?Ad`+$Eri35jk1NdN^@CYv!CvYAt(W0!kip~pnQMlWoE6OCZ25;+YosyMAy z#q}dEx>H$;f=WGD_xy`)*b4m670^1N+ZH%f&vdv%V@-zMW1}Af0(ul|y1u|*eJ7vg zsO|adF>KJrKI|7LxVKwg+gTT*&6Z`ApDHieZIJSK%f{PyM__o%*>qSpy`&+jvppqGC^a_|1W_2Zoz{{YZ3Jtf1rjjPbq$Ql4W`A!NW6g@ z>{LE2#`+C>R>0@DYT}wy-gSY}&hhvBTRa>Um5~zTywiw+_8o!m((ETdmBxbOuQlDU zR1bp9POFwuV$#giN|)oxotB#czm~#p-yNx?B*d)KY%Y6|OWE0rzVEhYn@$+Ip^2{q zjz3dVR;ngQ>e2s)vX!bX9OFHbXJ zeu`o5@hQ#nd6G|C0q{PmoC$fa@!9SCGt3`p%wfmb%5>%hm*L#E+qoyxnRjxA^WNRg zg-qufP9@G&<|#Qc#>>CJ%r5$XLL@~?DkPhdt7)9hiqrR^ab!1SoHO_!E6&~*jU&4$ z@yJ$Vk;F>Jno-!r(ph74@Q#0+p&JL8YoyG$qJNKxWa}25I-1P(CFK zrK#&G(Wms@<$oYEEMo|_F-x80P=be#xR&R4u7Np@G@Qjee`YA(C6afRBLhuJet{zs zZGN*CIkNCE_bkIUt$(n`g!LE)Oyu}$iN`pohCS&aL-T(F#UfJ@aqo3WpUgfjYL&eb zhyFBsojJ?wh5RO~Fk*^+2E8v0)J|#BG3AE`Y8xcn8Ytg`Ir#viH{|+ARg;FImRKy= z24yN$_1MG?#7FBsX!s<+@z?E}MHP0jbCeCxDQO(1& zOHw6m-@6c6T)}qIk_+deH*Zi(Ylusn300*Aa}14%rrlJdNrKkj`IE) vlJxursoaue@FA1N?$e&Wq=r(p%f>BZg<&iPyTZRq_=2+nM_=LwR#E&9O|~;$ literal 0 HcmV?d00001 diff --git a/tacit-app-api/target/classes/com/tacit/app/mapper/UserMapper.class b/tacit-app-api/target/classes/com/tacit/app/mapper/UserMapper.class new file mode 100644 index 0000000000000000000000000000000000000000..bc7b27cd6d10e3e1405d92fa2302d6f038ea47cb GIT binary patch literal 377 zcmb7AyH3ME5S$ATlK_I2Djg;J0Y*rnpfW-tf||9nG8fr**4-H?zed3a@KK13BV>t= zVjt4(%KAdDRE^Jao16+Ndi4b2@gY?;n3h>!|zNDyP6)$jZ`Va z)PQ!-l6`B}zNdCuaVp`(7Jo_e;-7>!17Lh!T(W69 zJfGj@2J@BgLnZgN(R=5krt95hgUhF00j<2)Xv;>H&iSMgyo&}`g%7nl!quCQ9h4D| g3$$01s|yAP8b@j=w97P4aE#ed=QzRjASXOwaT`v{l5c`D6ZWu_MMu}uT0u1qrVu(8#;tmVREGr?uW_G;iOsd zQe8Y! z8XNi%`gNH}rY%8dqH-;{t_UIf#06L!Uzf`r$r;LBQirQ<$k! literal 0 HcmV?d00001 diff --git a/tacit-app-api/target/classes/com/tacit/app/service/impl/UserServiceImpl.class b/tacit-app-api/target/classes/com/tacit/app/service/impl/UserServiceImpl.class new file mode 100644 index 0000000000000000000000000000000000000000..acd38c04d560fcb7484e8c7134391cbbe01f4c91 GIT binary patch literal 4596 zcmbtX`+pSG75`2)$!sik8{NGYA@_A8+K}95rMg`F#&$Nu3h@lCyRLs_J87^0FowU|9Ql_hOhb}DFbjS6y zYwLY>#Z!*Hz(=UJivr5|hyNdw42X)K|SV=gsSKlP{8J z#C5n{#bOQZ=upsh33&w#j>r<4jh0Zbw%yY|BYmcaJBV>2>~8>|icSTs)h7p`m6odL zR?s!I%(om_DfXvCUQ$V4U35%KI8IM_CJJ4_g7ZgCymn~f+yjSSJM{3mlLyZqJMga~ zNB?>9WDH%nQPTe=1@po|517$05mRz&KYiMOc0muFp6u&3^J05Xr)9rfy(G^Q_)w>i=u00^>v0bMmENY-REwWFFYqBEKx9AF>LP^mV#>n z49X{05k2r-1#|xTDJIWc!<;kkepE zen+{Fbq%X)7E_F|uk{Tjw` zw}SR5^8;j9(;e5aTz!p~m}`O|$=$nUct_721IfXj9mC1~9v&Mis9x}5cpu)c;sYA) zk!H=BGD|Kq`?7p^2_+>D$fEio1#{Ya!-*JZ61Z2xM{tm_q1Qe8^JYOvXQce}b2~<& zI7B*CYQ(vT)90Rk2gYp6%j-!EXUdYnWE;+E_@+c8R?MfFv8%_EX%#QvLot z4f*f9mj;XMGICelW`zDsGYCwVjhvfX)MBSRFyV7tizT{4?Gm{(nQt9{D5gW3L zg_Kxp${d^!B;)_LDWUVJ@L3n1fbxvDCZ(Lw{Futg{5Igyxr^Hn&73v4HPe;?H4W*BM+}L!;swx?e%lS#)h}zF`7y zd$GHOx0i6sqrqH4&QX2^^{nFB?Pt&vL3!2NaTe=&SpTxx*4pL`6d5>!;Rv>H6ys=X zWwec>?d8!Vb}Xwq3&EY4W%aG~TpMexZ>=jK+gd*f`!tFryw{k-2TS;HYyByDRSSv4 zAV1wepreF$0Re8qwdlrT+{h1^1Uvj5jy7_X!cEBGW;QXm;9e}nLu}NKVHu8N1uvSF zyh3hem~O+*u-Zd)osV3ADlxkL7AIS9~4Wq>+^2r)VztD$R$JDVRU;i1l2FW{f<^aFo1&LBSa6!rU1#u4NDIvSe9 z9P|>@_53RABL@cv%pe=0VQjz%e_K6>;=XMah_-uhNQj05h!!yffAAoZAV{M6YZ3(M zdLxwAq5H0Lk=;k&_(3`zp+J1f@nbahBD9r^h_RPAINA18o+{ISW@XWo+*H|$( zPF~#YnRInv?v#;oMjEN$kCk8?4QyH>VH+sni~U{YOnFj*-o@z?C45ajzh1&K^7&i| z6Y;JRo|h>5<}LJyl|ci>75E|#={+G+msCt$5+K_^LQ6b-^rBGygZX3) VHUEkFYw&0M#e4p>Qf2-I{12vFB@F-o literal 0 HcmV?d00001 diff --git a/tacit-common/pom.xml b/tacit-common/pom.xml new file mode 100644 index 0000000..8b822a7 --- /dev/null +++ b/tacit-common/pom.xml @@ -0,0 +1,98 @@ + + + + tacit-parent + com.tacit + 1.0.0-SNAPSHOT + + 4.0.0 + + tacit-common + Tacit Common + Common module for Tacit microservices + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.baomidou + mybatis-plus-boot-starter + + + + + com.oceanbase + oceanbase-client + 2.4.0 + + + + + io.jsonwebtoken + jjwt-api + + + io.jsonwebtoken + jjwt-impl + runtime + + + io.jsonwebtoken + jjwt-jackson + runtime + + + + + org.projectlombok + lombok + provided + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + true + + + + + \ No newline at end of file diff --git a/tacit-common/src/main/java/com/tacit/common/constant/CommonConstant.java b/tacit-common/src/main/java/com/tacit/common/constant/CommonConstant.java new file mode 100644 index 0000000..b167652 --- /dev/null +++ b/tacit-common/src/main/java/com/tacit/common/constant/CommonConstant.java @@ -0,0 +1,27 @@ +package com.tacit.common.constant; + +public class CommonConstant { + // 状态常量 + public static final Integer STATUS_ENABLE = 1; + public static final Integer STATUS_DISABLE = 0; + + // 删除状态 + public static final Integer DEL_FLAG_NORMAL = 0; + public static final Integer DEL_FLAG_DELETED = 1; + + // JWT相关常量 + public static final String JWT_HEADER = "Authorization"; + public static final String JWT_PREFIX = "Bearer "; + public static final String JWT_SECRET = "tacit_app_secret_key_2024"; + // 7天 + public static final Long JWT_EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000L; + + // 服务名称常量 + public static final String SERVICE_GATEWAY = "tacit-gateway"; + public static final String SERVICE_ADMIN = "tacit-admin"; + public static final String SERVICE_APP_API = "tacit-app-api"; + + // 角色常量 + public static final String ROLE_ADMIN = "admin"; + public static final String ROLE_USER = "user"; +} diff --git a/tacit-common/src/main/java/com/tacit/common/entity/ResponseResult.java b/tacit-common/src/main/java/com/tacit/common/entity/ResponseResult.java new file mode 100644 index 0000000..4298c3a --- /dev/null +++ b/tacit-common/src/main/java/com/tacit/common/entity/ResponseResult.java @@ -0,0 +1,47 @@ +package com.tacit.common.entity; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class ResponseResult implements Serializable { + private static final long serialVersionUID = 1L; + + private Integer code; + private String message; + private T data; + + private ResponseResult() { + } + + public ResponseResult(Integer code, String message, T data) { + this.code = code; + this.message = message; + this.data = data; + } + + public static ResponseResult success() { + return new ResponseResult<>(200, "操作成功", null); + } + + public static ResponseResult success(T data) { + return new ResponseResult<>(200, "操作成功", data); + } + + public static ResponseResult success(String message, T data) { + return new ResponseResult<>(200, message, data); + } + + public static ResponseResult fail() { + return new ResponseResult<>(500, "操作失败", null); + } + + public static ResponseResult fail(String message) { + return new ResponseResult<>(500, message, null); + } + + public static ResponseResult fail(Integer code, String message) { + return new ResponseResult<>(code, message, null); + } +} diff --git a/tacit-common/src/main/java/com/tacit/common/exception/BusinessException.java b/tacit-common/src/main/java/com/tacit/common/exception/BusinessException.java new file mode 100644 index 0000000..55f7efa --- /dev/null +++ b/tacit-common/src/main/java/com/tacit/common/exception/BusinessException.java @@ -0,0 +1,34 @@ +package com.tacit.common.exception; + +import lombok.Getter; + +@Getter +public class BusinessException extends RuntimeException { + private static final long serialVersionUID = 1L; + + private Integer code; + + public BusinessException() { + super(); + } + + public BusinessException(String message) { + super(message); + this.code = 500; + } + + public BusinessException(Integer code, String message) { + super(message); + this.code = code; + } + + public BusinessException(String message, Throwable cause) { + super(message, cause); + this.code = 500; + } + + public BusinessException(Integer code, String message, Throwable cause) { + super(message, cause); + this.code = code; + } +} diff --git a/tacit-common/src/main/java/com/tacit/common/feign/AdminFeignClient.java b/tacit-common/src/main/java/com/tacit/common/feign/AdminFeignClient.java new file mode 100644 index 0000000..c488c48 --- /dev/null +++ b/tacit-common/src/main/java/com/tacit/common/feign/AdminFeignClient.java @@ -0,0 +1,13 @@ +package com.tacit.common.feign; + +import com.tacit.common.entity.ResponseResult; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +@FeignClient(name = "tacit-admin", contextId = "adminFeignClient") +public interface AdminFeignClient { + + @GetMapping("/user/info/{id}") + ResponseResult getUserById(@PathVariable("id") Long id); +} diff --git a/tacit-common/src/main/java/com/tacit/common/feign/AppApiFeignClient.java b/tacit-common/src/main/java/com/tacit/common/feign/AppApiFeignClient.java new file mode 100644 index 0000000..de11dda --- /dev/null +++ b/tacit-common/src/main/java/com/tacit/common/feign/AppApiFeignClient.java @@ -0,0 +1,13 @@ +package com.tacit.common.feign; + +import com.tacit.common.entity.ResponseResult; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +@FeignClient(name = "tacit-app-api", contextId = "appApiFeignClient") +public interface AppApiFeignClient { + + @GetMapping("/user/info/{userId}") + ResponseResult getUserInfo(@PathVariable Long userId); +} diff --git a/tacit-common/src/main/java/com/tacit/common/handler/GlobalExceptionHandler.java b/tacit-common/src/main/java/com/tacit/common/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..e0e64ad --- /dev/null +++ b/tacit-common/src/main/java/com/tacit/common/handler/GlobalExceptionHandler.java @@ -0,0 +1,72 @@ +package com.tacit.common.handler; + +import com.tacit.common.entity.ResponseResult; +import com.tacit.common.exception.BusinessException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; + +import java.util.Objects; + +@ControllerAdvice +@ResponseBody +@Slf4j +public class GlobalExceptionHandler { + + /** + * 处理业务异常 + */ + @ExceptionHandler(BusinessException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseResult handleBusinessException(BusinessException e) { + log.error("业务异常: {}", e.getMessage()); + return ResponseResult.fail(e.getCode() != null ? e.getCode() : 400, e.getMessage()); + } + + /** + * 处理请求参数校验异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + log.error("请求参数校验异常: {}", e.getMessage()); + String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage(); + return ResponseResult.fail(400, message); + } + + /** + * 处理请求参数类型不匹配异常 + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) { + log.error("请求参数类型不匹配异常: {}", e.getMessage()); + return ResponseResult.fail(400, "请求参数类型不匹配"); + } + + /** + * 处理JSON解析异常 + */ + @ExceptionHandler(HttpMessageNotReadableException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseResult handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { + log.error("JSON解析异常: {}", e.getMessage()); + return ResponseResult.fail(400, "JSON格式错误"); + } + + /** + * 处理其他异常 + */ + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ResponseResult handleException(Exception e) { + log.error("系统异常: {}", e.getMessage(), e); + return ResponseResult.fail(500, "系统内部错误"); + } +} diff --git a/tacit-common/src/main/java/com/tacit/common/utils/JwtUtils.java b/tacit-common/src/main/java/com/tacit/common/utils/JwtUtils.java new file mode 100644 index 0000000..5e784a0 --- /dev/null +++ b/tacit-common/src/main/java/com/tacit/common/utils/JwtUtils.java @@ -0,0 +1,112 @@ +package com.tacit.common.utils; + +import com.tacit.common.constant.CommonConstant; +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import lombok.extern.slf4j.Slf4j; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.Map; + +@Slf4j +public class JwtUtils { + + private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(CommonConstant.JWT_SECRET.getBytes(StandardCharsets.UTF_8)); + + /** + * 生成JWT令牌 + * @param claims 自定义声明 + * @return JWT令牌 + */ + public static String generateToken(Map claims) { + Date now = new Date(); + Date expireDate = new Date(now.getTime() + CommonConstant.JWT_EXPIRE_TIME); + + return Jwts.builder() + .setClaims(claims) + .setIssuedAt(now) + .setExpiration(expireDate) + .signWith(SECRET_KEY, SignatureAlgorithm.HS256) + .compact(); + } + + /** + * 解析JWT令牌 + * @param token JWT令牌 + * @return 自定义声明 + */ + public static Claims parseToken(String token) { + try { + return Jwts.parserBuilder() + .setSigningKey(SECRET_KEY) + .build() + .parseClaimsJws(token) + .getBody(); + } catch (ExpiredJwtException e) { + log.error("JWT令牌已过期: {}", e.getMessage()); + throw new RuntimeException("令牌已过期", e); + } catch (UnsupportedJwtException e) { + log.error("不支持的JWT令牌: {}", e.getMessage()); + throw new RuntimeException("不支持的令牌格式", e); + } catch (MalformedJwtException e) { + log.error("JWT令牌格式错误: {}", e.getMessage()); + throw new RuntimeException("令牌格式错误", e); + } catch (SecurityException e) { + log.error("JWT令牌验证失败: {}", e.getMessage()); + throw new RuntimeException("令牌验证失败", e); + } catch (Exception e) { + log.error("JWT令牌解析异常: {}", e.getMessage()); + throw new RuntimeException("令牌解析异常", e); + } + } + + /** + * 从JWT令牌中获取用户ID + * @param token JWT令牌 + * @return 用户ID + */ + public static Long getUserIdFromToken(String token) { + Claims claims = parseToken(token); + return Long.parseLong(claims.get("userId").toString()); + } + + /** + * 从JWT令牌中获取用户名 + * @param token JWT令牌 + * @return 用户名 + */ + public static String getUsernameFromToken(String token) { + Claims claims = parseToken(token); + return claims.get("username").toString(); + } + + /** + * 从JWT令牌中获取角色 + * @param token JWT令牌 + * @return 角色 + */ + public static String getRoleFromToken(String token) { + Claims claims = parseToken(token); + return claims.get("role").toString(); + } + + /** + * 验证JWT令牌是否有效 + * @param token JWT令牌 + * @return 是否有效 + */ + public static boolean validateToken(String token) { + try { + Jwts.parserBuilder() + .setSigningKey(SECRET_KEY) + .build() + .parseClaimsJws(token); + return true; + } catch (Exception e) { + log.error("JWT令牌验证失败: {}", e.getMessage()); + return false; + } + } +} diff --git a/tacit-common/src/test/java/com/tacit/common/entity/ResponseResultTest.java b/tacit-common/src/test/java/com/tacit/common/entity/ResponseResultTest.java new file mode 100644 index 0000000..789621a --- /dev/null +++ b/tacit-common/src/test/java/com/tacit/common/entity/ResponseResultTest.java @@ -0,0 +1,82 @@ +package com.tacit.common.entity; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * ResponseResult类单元测试 + */ +public class ResponseResultTest { + + @Test + public void testSuccessWithData() { + String data = "test data"; + ResponseResult result = ResponseResult.success(data); + + assertEquals(200, result.getCode()); + assertEquals("操作成功", result.getMessage()); + assertEquals(data, result.getData()); + } + + @Test + public void testSuccessWithoutData() { + ResponseResult result = ResponseResult.success(); + + assertEquals(200, result.getCode()); + assertEquals("操作成功", result.getMessage()); + assertNull(result.getData()); + } + + @Test + public void testFailWithMessage() { + String message = "自定义错误消息"; + ResponseResult result = ResponseResult.fail(message); + + assertEquals(500, result.getCode()); + assertEquals(message, result.getMessage()); + assertNull(result.getData()); + } + + @Test + public void testFailDefault() { + ResponseResult result = ResponseResult.fail(); + + assertEquals(500, result.getCode()); + assertEquals("操作失败", result.getMessage()); + assertNull(result.getData()); + } + + @Test + public void testFailWithCodeAndMessage() { + int code = 400; + String message = "参数错误"; + ResponseResult result = ResponseResult.fail(code, message); + + assertFalse(result.isSuccess()); + assertEquals(code, result.getCode()); + assertEquals(message, result.getMessage()); + assertNull(result.getData()); + } + + @Test + public void testEqualsAndHashCode() { + ResponseResult result1 = ResponseResult.success("test"); + ResponseResult result2 = ResponseResult.success("test"); + ResponseResult result3 = ResponseResult.success("different"); + + assertEquals(result1, result2); + assertNotEquals(result1, result3); + assertEquals(result1.hashCode(), result2.hashCode()); + } + + @Test + public void testToString() { + ResponseResult result = ResponseResult.success("test"); + String toString = result.toString(); + + assertNotNull(toString); + assertTrue(toString.contains("success")); + assertTrue(toString.contains("test")); + } +} diff --git a/tacit-common/src/test/java/com/tacit/common/utils/JwtUtilsTest.java b/tacit-common/src/test/java/com/tacit/common/utils/JwtUtilsTest.java new file mode 100644 index 0000000..d10f644 --- /dev/null +++ b/tacit-common/src/test/java/com/tacit/common/utils/JwtUtilsTest.java @@ -0,0 +1,75 @@ +package com.tacit.common.utils; + +import com.tacit.common.constant.CommonConstant; +import io.jsonwebtoken.Claims; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * JWT工具类单元测试 + */ +public class JwtUtilsTest { + + private String token; + private Map claims; + + @BeforeEach + public void setUp() { + claims = new HashMap<>(); + claims.put("userId", 1L); + claims.put("username", "testuser"); + claims.put("role", "admin"); + token = JwtUtils.generateToken(claims); + } + + @Test + public void testGenerateToken() { + assertNotNull(token); + assertTrue(token.length() > 0); + assertTrue(token.contains(".")); + } + + @Test + public void testParseToken() { + Claims parsedClaims = JwtUtils.parseToken(token); + assertNotNull(parsedClaims); + assertEquals(1L, parsedClaims.get("userId")); + assertEquals("testuser", parsedClaims.get("username")); + assertEquals("admin", parsedClaims.get("role")); + } + + @Test + public void testValidateToken() { + boolean isValid = JwtUtils.validateToken(token); + assertTrue(isValid); + } + + @Test + public void testValidateTokenWithInvalidToken() { + boolean isValid = JwtUtils.validateToken("invalid.token.here"); + assertFalse(isValid); + } + + @Test + public void testGetUserIdFromToken() { + Long userId = JwtUtils.getUserIdFromToken(token); + assertEquals(1L, userId); + } + + @Test + public void testGetUsernameFromToken() { + String username = JwtUtils.getUsernameFromToken(token); + assertEquals("testuser", username); + } + + @Test + public void testGetRoleFromToken() { + String role = JwtUtils.getRoleFromToken(token); + assertEquals("admin", role); + } +} diff --git a/tacit-common/target/classes/com/tacit/common/constant/CommonConstant.class b/tacit-common/target/classes/com/tacit/common/constant/CommonConstant.class new file mode 100644 index 0000000000000000000000000000000000000000..f5abb216cdb813d8b91e492e8b5c5d11527df41c GIT binary patch literal 1190 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 E8`N^~o&W#< literal 0 HcmV?d00001 diff --git a/tacit-common/target/classes/com/tacit/common/feign/AdminFeignClient.class b/tacit-common/target/classes/com/tacit/common/feign/AdminFeignClient.class new file mode 100644 index 0000000000000000000000000000000000000000..dc3cf8b9ddb6dc5173fc124dc2c9ff3365d9ff29 GIT binary patch literal 692 zcmbVKO-}+b5S=25hzfqks~7d4jfwFf#sp0;Ca44=7f)q(Sc|*urtN~n_{Ti>1N>3O zSx6MrgYnR`oxXYRrElKeUtR&A0vkEVFxd8V#GUY@<8+F&;vJNM;uSxVs+u0pLy5}e zAj_cC6+OX2p#px>?xN=y6asX&7R}k<(r0kIQ~QF{vq&*2; znL)luYQiN38C?IH=w!^n$YlAH$Tu4a$wt_cR<=W|D5afnk_<37tZ5T)8yl&Dju8>| zwRzxuZ1c8MJ{O-@eu3^v#4+KBa=ItNguE7cLLb1T>S+EX{bvSSlVWv|k~%aKsz-lT ztqXVG5=II#PP!RMKRwsfiSe*1Lvpb8J+1vT1qKJdN_e48e6C|uqo~JHCibi%uaxU> zCiX-+1{95_mBUAuW?UN9#+qgrFqop50$?;G(=bD~9NjW73whcX>3xn?;SQLEd03#@ eeA<*g6f6`8SsLP(hWHYdm#Ma5VHMUatbYI&9?;tW literal 0 HcmV?d00001 diff --git a/tacit-common/target/classes/com/tacit/common/feign/AppApiFeignClient.class b/tacit-common/target/classes/com/tacit/common/feign/AppApiFeignClient.class new file mode 100644 index 0000000000000000000000000000000000000000..50c6877da2e8034fc125ad76de17b94c52d7a24f GIT binary patch literal 691 zcmbVKO-}+b5S=0)0)ilF;?)x#v@tOr#F!8h3<)ZM$i-84cUX%{yXkg8WBg+t`~m(b z<18c!!GrP8b~=51@3k`@pKtE~(1hI_q!`p)J?2)p(sGK%TJa%DU-4!bHbdD;;>$py zvN=dIsEou!@IWY^-#R07ErX(u_AWxxRzuC;tls&7bhPpt2b~oKVw3in_b>{zijdYg zunh+JKB);C8)R_vZ=&-B2Xm8+3nJg^#Uwk%fsCXRU{fh=g_UH0!AVCOpGTpQ${!jr z#;G=se2NZtr1H4<#_}t)*CGrFN0ga~2x9VD<1u{zm-GYtB?;T!D}()I!LCS%Et)0e z(?2VBg?$(ZBL$f!?LuG2#>JKl$iVJOwT_Z1FgX5|-VJo@aUG(X*LtDK#GF>-lR;_L z6o>R!8j_0RYGv`swmk+_$}V?DmH~q`>MH<7eUgDJ?Q*nB!8+t=Ez@~{M)4k41RGGI bTrRm(o`E)Jb7cmtP_{}Jw<4&)b_6?L42IIt literal 0 HcmV?d00001 diff --git a/tacit-common/target/classes/com/tacit/common/handler/GlobalExceptionHandler.class b/tacit-common/target/classes/com/tacit/common/handler/GlobalExceptionHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..63cd5bae5f73ec35b3af9e19078a5d8b7b89bec4 GIT binary patch literal 4659 zcmc&%TXz#x6#h;xBn_dZg@UvwRBdUYFo1|aYO$9TioHluB8pn4$uylZnF*6gp@J8R zMY&XPEq%}jgyjP+UjSJPT4A|-^1-6P9+dg90xKEnbM#4B4MKA|C96^@ zW$R?{#U-PYmc+Qku(GzUW5&9$q04GKuoSDX+Joo(Sc3}E|8H0fg_5poI>UzA+|!N~ z(_4YM2uHlukLU3MgNO9DXfaY=K1+GKYDnB>AJ(JFgBSgH2`@90&cL&FFnDFvh=@v> zWRU;c`H31oHlUWI5ApTxeuh=G?M~Zf#BanV59@YZlS>S5Wmx;@#@(q~Ba@d#roO#7b@kfx&ljC+Z|7{k z!qAW*BFQ=G*TsZ%T+@#V$E7}@PgY|>r(_Ihu_is9PDrZJrEw3*F`IZFw&PVlcH%W+ zlqhJXGbAW$-d5tmO+2|%j)^>4g;^&kNOoh72LV4C@Hz$IVh%GbBLr=-q{M<|AZ?ys z{mga>k-d1sgC;+k(UJ|3v>_{2ex?|Tb?I1I)}<~@?Mf>|T#b{sY+yQwvz9yvGStmu z%nTGCTCvZM{X7p#$);ARU!-uc^2>_@4Ao9@AKtn*dF8@`@v+H^6Vsz(PL?}3%bg6J zZpt~!i44JlsH&P_GBTU;p5dg_DW?*m5go8;_oADj@-Yy+I7nsSK)Ac>(e)pvu3UCP z3Uf$349#wk1`H!f1x_83bc1T!ewtclQ|yH#F(&pYk^@JC;*P7Cx_W=|{-x<}zI$}z zh8Krv_2KP%5AXfpRQxVie1u`eO!C{3cy)T8m!=OGb~{P6QZJX&+?BeB7avl^wKSg{ zoj&uEiO7dOL_LW4A%Tj>>Wt-%lH4Xn4NV_rC?dAaDoqqSVYDb>DiyG@$#F73HN>oY zB^^|gyV8k1N$=rK%9Q@7s6<3v=5uSYz!;DzlDFlSlE?QGQncFK;Zpq%*XD|o=57~c z#{vr(10zGWxNLunbSea4>Xb)2#iV6)QCN`w3r$|J#>(NXK^2D?EEp2XU z?F|JF9twsjZ>!BPm|Z4vIs?N%t*dz3ymx=PExg)J-1Tci3PVyWUl(%>F1Vdd-9lhaTC=v|y z21B9l5M41EqKb9J^M$pv9+lc;-l;3y?{HiAAC<+li55*YbWNdKVN+~Kj!IPHce)0K zU4$JyCfgpGwb*a~RdoH#ql+s;1;2pO$U|egxzaOEGlAZW-h~_Qg59D)9tP;iKLaZv z)6;Kmz#xv&j5;q8$j=;3(wht4{0lr~%QoFX$pp&tL4(Raz~_~Bu4T%#dP=MidLDzEJY+1Dl}43yvU4(B{BVX}hD6+;Psv zaoz>TxfvW6C}A$z=}|?;_^+tUI}Y(SJUm6{KM_666j?zq$7q&^uQGBuHJ5098Tbba CT&fKK literal 0 HcmV?d00001 diff --git a/tacit-common/target/classes/com/tacit/common/utils/JwtUtils.class b/tacit-common/target/classes/com/tacit/common/utils/JwtUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..86be20ce589adbf823be94a5658aa41ac0683913 GIT binary patch literal 4729 zcma)A340UQ6+I(kMwZ7cW(%v$?gbDb5Ehd(;Ml=N4#bj82uankJjgPVMjeeXZW3D3 zP)O@+EnSkPSsK!`DJ{XISPnF#dz&`h_kF>}^hfmTc{5{aEQ^qjPkwLa-FNPN_nv#- z9p3rhn{NTwf&cp8L5Ykh3Q93m!i-+^uXOvelvGZj=~mV}y!p75J$Bxd?K zOXx`s$+W2^O@E{H+~_g_m{QYP+v&q%ERj*Apc+dh%#P{)-n5?V)4ELkke2kf z_L*s(r7IJQcWZ`(B{j8yA~HMe^@egR#|jxM6|BN)2@9?-N~_YE*%()22`X4yW1q=# zXPcU8xS^I>1$C&GP)?MVbULGT?_ikLxp72TZVa?m!8!p{L7=81shFXfF+Ev^^|)Ea z1_ifZqlBtS@e-D%g?l>0M1n8a)Y#q>3h!>(FX1NZG)Mdqqd#Tp{-72yG;_Ds-%y5I zv026z1zWLA!m>jD1Y^;pYGw>=M?9($Vp!=_s$teE8Z$z3ldcA-T^tAgDKNT@FK93wGdgB?)eA*$zTC2TI{gQDk~0Ay*t zTfrXeWh|^jkg)i@Oy-Vu1Z9L2bf8nh{Oj7vT(x{*r$K8Uc~t9IX{E2@ykbVCspVem zmvNth1GrzptjStL=CTUy)Vq27iD9wpT7!%aN|;-?(>6!Oha@a7T(eR{>!!P#jzqN7 zc&>CQh@hLbF0QlSR0eb->QBcHZtV33^k`Hw8oW42O{`^CE}p*n*ucop;MnC8qtCst zt@^=-eF&pRK}_(-M}yn6bXtvS>XNu_kd zoajO?K0=;c$v!@M>g?#iv8&HMn)8(Hpg#0?@h~fkTgEnY^u9uMLcy zJvMUs+}Inh*x<(n_@{E@3f%N?Xn}7_B8-zKG|@chXhDDmT

DGUctP;oYwC7(i_7~Z1z*O?yb>+@>`Qx&8*{u6vz(2sMYdFlX)4xrH4eHa{0}NyfJoyoJjXdNBD#MT^Zj~@O}J%RlYEt+}9`3DtZ!XB-o?!dTADAXi?39opv0G zT>W%^qu$MSaC#t?)b7qCx-=ssYK2uUBWk=;HDcn~c{#=GiBZSWzztfD2HFwTk{bIK z&0+R(=ajB)DxXvimB{9Z(|k?0otu*;tCMk=GRu?W9QY3&C%$LX^cFW^5BF{Ul3e{}r35N?S+?nfm z#O)-jR)~(Zkn2w2W8>h!iZbE?o>23O0O!0vgZtZAcwBocSh-@|dBPU0b@wrIw@2d6 zcb6}yXN-u}91{ssG2VBt6K=s$mUs`pf^6wA%aG>nE44C4b5%Pt5c6tAKJ@SDwx`B=z*Rak_T z{2;5x5`HICp@AP|cksP1tlCzNcky#RdHMbD3;dGCE+opYIP>7wd~ch-gHpfY({K6n z;CIx*+mEt5hV(S1@E!0{7LBxQ7a|jO$gE1UD%8Swy@LOiify*g3dpQ?R491miuofYBj_3d;Ecp+-A-G$XUpm{fV=r zHTyGXC#~6EIHQ~KSNx6R-}#%1(y)v$d-gv5j`DY$zX68lANVJw1azGu zpuY0+!+6Gn3;0qNFJ^ICaK|RC=p}%==%ihifphI`j=EC*H^aO57eUMLZ~VsrZ=sCX zHPZqA>Nt2I>^+2Scfrog!74e}E7yg6?Ru~W2z&nxU`H_OSo;LSDy*fk#hAKp${CA^ zf>kzmO3rYn$fx-^N-Sfh00$YoC@IlHO2jY^y_7h_8WBeggD$d5cy9q_FuZoy>Re%C zd9cFd#Z}_iVY + + + tacit-parent + com.tacit + 1.0.0-SNAPSHOT + + 4.0.0 + + tacit-gateway + Tacit Gateway + Gateway Service for Tacit Microservices + + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + org.springframework.boot + spring-boot-starter-security + + + + + com.tacit + tacit-common + ${project.parent.version} + + + + + org.projectlombok + lombok + provided + + + + + org.apache.commons + commons-lang3 + 3.14.0 + + + + + org.springframework.boot + spring-boot-starter-logging + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + false + + + + + \ No newline at end of file diff --git a/tacit-gateway/src/main/java/com/tacit/gateway/GatewayApplication.java b/tacit-gateway/src/main/java/com/tacit/gateway/GatewayApplication.java new file mode 100644 index 0000000..76f7e76 --- /dev/null +++ b/tacit-gateway/src/main/java/com/tacit/gateway/GatewayApplication.java @@ -0,0 +1,13 @@ +package com.tacit.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +@SpringBootApplication +@EnableDiscoveryClient +public class GatewayApplication { + public static void main(String[] args) { + SpringApplication.run(GatewayApplication.class, args); + } +} diff --git a/tacit-gateway/src/main/java/com/tacit/gateway/filter/JwtAuthenticationFilter.java b/tacit-gateway/src/main/java/com/tacit/gateway/filter/JwtAuthenticationFilter.java new file mode 100644 index 0000000..cf0b9c6 --- /dev/null +++ b/tacit-gateway/src/main/java/com/tacit/gateway/filter/JwtAuthenticationFilter.java @@ -0,0 +1,104 @@ +package com.tacit.gateway.filter; + +import com.tacit.common.constant.CommonConstant; +import com.tacit.common.utils.JwtUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.List; + +@Component +@Slf4j +public class JwtAuthenticationFilter implements GlobalFilter, Ordered { + + // 不需要认证的路径 + private static final List WHITE_LIST = List.of( + "/api/auth/login", + "/api/auth/register", + "/swagger-ui", + "/v3/api-docs" + ); + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + String path = request.getURI().getPath(); + + // 检查是否在白名单中 + if (isWhiteList(path)) { + return chain.filter(exchange); + } + + // 获取Authorization头 + HttpHeaders headers = request.getHeaders(); + String authorization = headers.getFirst(HttpHeaders.AUTHORIZATION); + + // 检查Authorization头是否存在 + if (StringUtils.isBlank(authorization)) { + return unauthorizedResponse(exchange, "缺少认证令牌"); + } + + // 检查Authorization头格式 + if (!authorization.startsWith(CommonConstant.JWT_PREFIX)) { + return unauthorizedResponse(exchange, "认证令牌格式错误"); + } + + // 提取JWT令牌 + String token = authorization.substring(CommonConstant.JWT_PREFIX.length()); + + // 验证JWT令牌 + try { + JwtUtils.validateToken(token); + + // 从令牌中获取用户信息并添加到请求头 + Long userId = JwtUtils.getUserIdFromToken(token); + String username = JwtUtils.getUsernameFromToken(token); + String role = JwtUtils.getRoleFromToken(token); + + // 将用户信息添加到请求头 + ServerHttpRequest mutatedRequest = request.mutate() + .header("X-User-Id", String.valueOf(userId)) + .header("X-Username", username) + .header("X-Role", role) + .build(); + + return chain.filter(exchange.mutate().request(mutatedRequest).build()); + } catch (Exception e) { + log.error("JWT认证失败: {}", e.getMessage()); + return unauthorizedResponse(exchange, e.getMessage()); + } + } + + /** + * 检查路径是否在白名单中 + */ + private boolean isWhiteList(String path) { + return WHITE_LIST.stream().anyMatch(path::startsWith); + } + + /** + * 返回未授权响应 + */ + private Mono unauthorizedResponse(ServerWebExchange exchange, String message) { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.UNAUTHORIZED); + response.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/json"); + String responseBody = String.format("{\"code\": 401, \"message\": \"%s\"}", message); + return response.writeWith(Mono.just(response.bufferFactory().wrap(responseBody.getBytes()))); + } + + @Override + public int getOrder() { + return -100; + } +} diff --git a/tacit-gateway/src/main/resources/application-dev.yml b/tacit-gateway/src/main/resources/application-dev.yml new file mode 100644 index 0000000..df67938 --- /dev/null +++ b/tacit-gateway/src/main/resources/application-dev.yml @@ -0,0 +1,50 @@ +server: + port: 8080 + +spring: + cloud: + gateway: + discovery: + locator: + enabled: true + lower-case-service-id: true + routes: + # Admin Service Route + - id: tacit-admin + uri: lb://tacit-admin + predicates: + - Path=/admin/** + filters: + - StripPrefix=1 + + # App API Service Route + - id: tacit-app-api + uri: lb://tacit-app-api + predicates: + - Path=/api/** + filters: + - StripPrefix=1 + - JwtAuthenticationFilter + + # Swagger UI Routes + - id: swagger-admin + uri: lb://tacit-admin + predicates: + - Path=/swagger-admin/** + filters: + - StripPrefix=1 + + - id: swagger-app-api + uri: lb://tacit-app-api + predicates: + - Path=/swagger-app-api/** + filters: + - StripPrefix=1 + +# Logging Configuration +logging: + level: + org.springframework.cloud.gateway: debug + org.springframework.http.server.reactive: debug + org.springframework.web.reactive: debug + reactor.netty: debug diff --git a/tacit-gateway/src/main/resources/bootstrap.yml b/tacit-gateway/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..0df105b --- /dev/null +++ b/tacit-gateway/src/main/resources/bootstrap.yml @@ -0,0 +1,16 @@ +spring: + application: + name: tacit-gateway + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: public + config: + server-addr: localhost:8848 + namespace: public + file-extension: yml + group: DEFAULT_GROUP + refresh-enabled: true + profiles: + active: dev diff --git a/tacit-gateway/target/classes/application-dev.yml b/tacit-gateway/target/classes/application-dev.yml new file mode 100644 index 0000000..df67938 --- /dev/null +++ b/tacit-gateway/target/classes/application-dev.yml @@ -0,0 +1,50 @@ +server: + port: 8080 + +spring: + cloud: + gateway: + discovery: + locator: + enabled: true + lower-case-service-id: true + routes: + # Admin Service Route + - id: tacit-admin + uri: lb://tacit-admin + predicates: + - Path=/admin/** + filters: + - StripPrefix=1 + + # App API Service Route + - id: tacit-app-api + uri: lb://tacit-app-api + predicates: + - Path=/api/** + filters: + - StripPrefix=1 + - JwtAuthenticationFilter + + # Swagger UI Routes + - id: swagger-admin + uri: lb://tacit-admin + predicates: + - Path=/swagger-admin/** + filters: + - StripPrefix=1 + + - id: swagger-app-api + uri: lb://tacit-app-api + predicates: + - Path=/swagger-app-api/** + filters: + - StripPrefix=1 + +# Logging Configuration +logging: + level: + org.springframework.cloud.gateway: debug + org.springframework.http.server.reactive: debug + org.springframework.web.reactive: debug + reactor.netty: debug diff --git a/tacit-gateway/target/classes/bootstrap.yml b/tacit-gateway/target/classes/bootstrap.yml new file mode 100644 index 0000000..0df105b --- /dev/null +++ b/tacit-gateway/target/classes/bootstrap.yml @@ -0,0 +1,16 @@ +spring: + application: + name: tacit-gateway + cloud: + nacos: + discovery: + server-addr: localhost:8848 + namespace: public + config: + server-addr: localhost:8848 + namespace: public + file-extension: yml + group: DEFAULT_GROUP + refresh-enabled: true + profiles: + active: dev diff --git a/tacit-gateway/target/classes/com/tacit/gateway/GatewayApplication.class b/tacit-gateway/target/classes/com/tacit/gateway/GatewayApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..860313ea582b5c9b2e513282943acc4c1b76bb83 GIT binary patch literal 787 zcma)4&rcLF6#lv_%(9Gvi-P#$U}DU&2Yd6fh6GmdGSS2}>%mi}TgKu{yXka5{wz-r z5B>rEQH0k4!z9kZ9^Om8ukUO7z4zzp*KYv(c-}_`OA)#;ma)RHaUxCy&xOhO$Mi%= z&#>}V8}0WPmWI2t2t9_E(iYqcsXfnxS7+j!zrV@*i$$)b@Y;v#1;K$HGju0*t{B!6ZPbUVNR|5>&N2)VD@8sNPKW(1*!ACZ$?z)q9|wYZ zAvFDDYuNVi8S%npq_^2ZGxVpna#9`X@SN>`9ey3$Vc7Xp8Lta9)1?l-G{*Y610=lH zYEEstsJtbYYIoFa6Aq{!4VUNx2dx4!x7D1>Tr1=GT$j?GDtFEgO|a_S-~FWK$9RE= zhAgZOSQs;k>$KyP8Rg~C1=!D;-k=z0U=q0z$@wh8axaUFN?gmO6hl=3s+8glK`IsgCw literal 0 HcmV?d00001 diff --git a/tacit-gateway/target/classes/com/tacit/gateway/filter/JwtAuthenticationFilter.class b/tacit-gateway/target/classes/com/tacit/gateway/filter/JwtAuthenticationFilter.class new file mode 100644 index 0000000000000000000000000000000000000000..517184763c41be480c2a2aee56d95eb8e9328ff6 GIT binary patch literal 6979 zcmd5>d3+pY8GgRaF`Lb_O_yGqLP^tuq-~fUK_Dqdw^ss5TbeYXtsIlxNiywbXPKQ% zD2RySfpQ8SAS!54QKSbLl2Y&%MZ7P(Q7J9riT45cd^3~H&~B1i|FQl3W@f+R{oeO^ z&+pBXFCTauz$#H6K?p?(idB@LRG?yq-mhyZJ>8>i?Al=@9D&l6X4-Vl6)38y?I=eW zWeOrH%ApD@wd@`(o3YJwPq(f283UHRQyVb4w5(zG8@ATwzB-MrwO1y3Nz@RC^cc=& zQAit(#*xMdreV5*87gLCmVg?Z6i~QtlkW5iOdHwg!q?_VRJgekvlX1I;uOpw zUFbM%DiXg#`P4CuSGZZqW&stX33RXL~ENIPaicT6k2&K(yhH?y6+ren0260zwu z9&^DnYPXf63Ud`ytC)xR0*!@$NTjS>^5|37drWv|t?t#$G?DN)61b?QpmN63Z)0tX zt1rve5|(XfnOs-O%=S{gRx53}3R|e+G}L&8&}8UI!_ErSjAsZ^<^IY>1&ajc7n0Al znZVL8mIzFem|C`Zl?!PEwK!dchNS{w>TR8AOH3CQ!t_{}BUp|V3hGsyf!Fv<=ovlH zYf#p{J}aGd9cP8+seX%NrYMjZZ^zgw0hxXof z&m*mC!M9) z_vN6wL~s!{D`->EE;%~&7_o9vbJ8Jw6;A5cQ)ZH$(r)cE(sFx;icVZijmer68^cHv#f$5tTy@P!iEX!v9uRAVK;(>#-AB2M zmnrD_qshY;yBs^PQ$b2a9~fEUWBToJvqyP*-Jl_`vjEQ9kLBTi$uI#>GBi{V6se{# zY;tmOoz#jH(ovC<39+P0qA0WEfK<&U~gkZw#ZG(Dc1j+gOJEcu2v+Dn5llfq8{IAzy{GF(~HV~TNrOTF7Rrh(0$luiqveop{IKHCdNqm(K&~4d$I%~JH z3z%In35+;jz#siXrDR`I@pXKI5Ow9cyA69CZyJ`pi-C3wOL1#j)6%@<+8W)_8gbA(=5gfvE3J$9{g6A15jt<9p-IdYjy^v3D3v$0YrDwAv2`muE zS@FCeFh$cdrX~w)jTyvDhw)pck@a@MxT0kTbeUD^a%LERq;qKfE9Ba` zq?O3Z==~=Zf5u+~LKdr~Z9z7C5CaDJUyKAJs+%bk{7vADf~HjPzL&DP^ptnqQ}7Rg zs)AeP)or8AYS56iV4K;KW-zlEJLVsisa6KW@cruCMjDS7X{NN8X=6jKugkF8^{y26 zGcPCfREKVx^0z-&?DU#+lI1Opj(O6}g@f66MNwnH5PY21;|Z*kktxR_s+q9) zN!`79xFtU$f}E4(qXc5Kj`4vbY&OeDW@WFU=N->)ldLSr@q`J$-A#-HX-*HEm#>>R z7dG;pndJpf85c>`KQPQe#WC(?0nTy7}*Jx@7F90O(Ue@R+6bL zcOozxW2@t~3ku36`z>xrp>Qh`DZi%}X|r;6!eBt6jh;M45A1Y#yg+Poj?J+?qr=Ra z3?8e}X^S-$OG;YHqJq{UQPY4bU$rQYtj3!67j6s*SQ33obwYN$<$<_Xe-@KIPSVD%HlMP8`+d zblR}pv?_}n#qS^}f-Q>$$DmV4P(>_f**E@DhExv~%;o(l#Q%TA-}$VR_dVW2D&>U= z0{`T92>(JUzuC*?^WPlN_!fMZEP4cDpS$EGKIJqp126NbdJFK19OCyp!L%(mhtA+S zm18e19)=>Yp>7zH1O{jw_|92DC;a;rAJ~R=NeiUlfzXWa?7E z3%G_m2pOIcQ%I$ld_N&7#8jF}JsuZPj+K(yDT%~PVT;cd#x7IV3m_Q{qopOW?MTn zQvQMEGRkr}>e$tiYO42+%rmw=5BhpBgAhv4&lfYrEXrL+HN{-eJ!C2&#B8eGL#B>z z>3KyJcOQ*Gcfdc3{G-C+