Compare commits
38 Commits
2617b4a5ae
...
master
Author | SHA1 | Date | |
---|---|---|---|
a663a73ff1 | |||
2d27ebea35 | |||
d8b7943646 | |||
f58c1813ff | |||
57253951d3 | |||
6aec72e6ab | |||
6c1f6d824e | |||
c668251a3e | |||
da38955955 | |||
3afb26202b | |||
c58105b8ca | |||
ef5f9f2853 | |||
dcc6821d10 | |||
cd98de9213 | |||
c4ddec8775 | |||
f888dedfcd | |||
30281a40b5 | |||
e5252961bd | |||
8af10ecb4e | |||
9eec90ef98 | |||
bd06fc047d | |||
4ab23b99c8 | |||
31639a6e98 | |||
2aae88278c | |||
f04ab7a947 | |||
b6bcc69a83 | |||
6a579104ba | |||
8535dce094 | |||
057a066404 | |||
158a93d608 | |||
157afecd35 | |||
71cdb894c0 | |||
41931a7f9a | |||
264b9a528a | |||
06cba204b5 | |||
277fbd22cd | |||
6d3f837d7c | |||
76102067ab |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target/
|
@ -1,4 +1,4 @@
|
||||
activeProfiles=pom.xml
|
||||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
||||
|
@ -1,59 +1,68 @@
|
||||
package co.jp.app.common;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class ApiResponse<T> {
|
||||
//成功状况判定
|
||||
private boolean success;
|
||||
//状态码
|
||||
private int code;
|
||||
//状态信息
|
||||
private String message;
|
||||
//数据
|
||||
private T data;
|
||||
|
||||
public static <T> ApiResponse<T> success(T data) {
|
||||
|
||||
return new ApiResponse<>(true, null, data);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> fail(String message) {
|
||||
|
||||
return new ApiResponse<>(false, message, null);
|
||||
}
|
||||
|
||||
public ApiResponse() {
|
||||
}
|
||||
|
||||
public ApiResponse(boolean success, String message, T data) {
|
||||
this.success = success;
|
||||
this.message = message;
|
||||
private ApiResponse(ResultCode resultCode, T data) {
|
||||
this.code = resultCode.getCode();
|
||||
this.message = resultCode.getMessage();
|
||||
this.success = (resultCode.getCode() == ResultCode.SUCCESS.getCode());
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
private ApiResponse() {
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> fail() {return success(null);}
|
||||
|
||||
public static <T> ApiResponse<T> success(T data) {
|
||||
return new ApiResponse<>(ResultCode.SUCCESS, data);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> fail(ResultCode resultCode) {
|
||||
if (resultCode == ResultCode.SUCCESS) {
|
||||
|
||||
throw new IllegalArgumentException("Cannot use SUCCESS ResultCode for fail method. Use a specific error code.");
|
||||
}
|
||||
return new ApiResponse<>(resultCode, null);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> fail(ResultCode resultCode, T data) {
|
||||
if (resultCode == ResultCode.SUCCESS) {
|
||||
throw new IllegalArgumentException("Cannot use SUCCESS ResultCode for fail method. Use a specific error code.");
|
||||
}
|
||||
return new ApiResponse<>(resultCode, data);
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ApiResponse{" +
|
||||
"success=" + success +
|
||||
", code=" + code +
|
||||
", message='" + message + '\'' +
|
||||
", data=" + data +
|
||||
'}';
|
||||
|
87
src/main/java/co/jp/app/common/ResultCode.java
Normal file
87
src/main/java/co/jp/app/common/ResultCode.java
Normal file
@ -0,0 +1,87 @@
|
||||
package co.jp.app.common;
|
||||
|
||||
public enum ResultCode {
|
||||
|
||||
SUCCESS(200, "Success"),
|
||||
|
||||
// 客户端错误段 (1000 - 1999)
|
||||
BAD_REQUEST(1000, "HTTP 400 Bad Request"),
|
||||
UNAUTHORIZED(1001, "HTTP 401 Unauthorized"),
|
||||
FORBIDDEN(1002, "HTTP 403 Forbidden"),
|
||||
NOT_FOUND(1003, "HTTP 404 Not Found"),
|
||||
METHOD_NOT_ALLOWED(1004, "HTTP 405 Method Not Allowed"),
|
||||
REQUEST_TIMEOUT(1005, "HTTP 408 Request Timeout"),
|
||||
CONFLICT(1006, "HTTP 409 Conflict"),
|
||||
UNSUPPORTED_MEDIA_TYPE(1007, "HTTP 415 Unsupported Media Type"),
|
||||
TOO_MANY_REQUESTS(1008, "HTTP 429 Too Many Requests"),
|
||||
VALIDATION_ERROR(1009, "Parameter validation failure"),
|
||||
|
||||
// 服务端错误段 (2000 - 2999)
|
||||
INTERNAL_SERVER_ERROR(2000, "HTTP 500 Internal Server Error"),
|
||||
SERVICE_UNAVAILABLE(2001, "HTTP 503 Service Unavailable"),
|
||||
GATEWAY_TIMEOUT(2002, "HTTP 504 Gateway Timeout"),
|
||||
DATABASE_ERROR(2003, "Database error"),
|
||||
NETWORK_ERROR(2004, "Network error"),
|
||||
THIRD_PARTY_SERVICE_ERROR(2005, "Third-party service error"),
|
||||
|
||||
// ================================== 用户模块状态码 (3000 - 3999) ==================================
|
||||
// 注册相关
|
||||
// USER_REGISTRATION_SUCCESS(3000, "用户注册成功"),
|
||||
USER_EMAIL_ALREADY_EXISTS(3001, "Email already exists"),
|
||||
|
||||
USER_USERNAME_ALREADY_EXISTS(3002, "Username"),
|
||||
|
||||
USER_EMAIL_NOT_VALID(3006, "Email is not valid"),
|
||||
|
||||
USER_PASSWORD_TOO_SHORT(3003, "password too short"),
|
||||
USER_PASSWORD_TOO_WEAK(3004, "password too weak"),
|
||||
USER_REGISTRATION_FAILED(3005, "User registration failed"),
|
||||
|
||||
// 登录相关
|
||||
// USER_LOGIN_SUCCESS(3100, "登录成功"),
|
||||
USER_ACCOUNT_NOT_FOUND(3101, "User account not found"),
|
||||
USER_INVALID_CREDENTIALS(3102, "User invalid credentials"),
|
||||
USER_ACCOUNT_LOCKED(3103, "User account locked"),
|
||||
USER_ACCOUNT_DISABLED(3104, "User account disabled"),
|
||||
USER_ACCOUNT_EXPIRED(3105, "User account expired"),
|
||||
USER_LOGIN_FAILED(3106, "User login failed"),
|
||||
USER_SESSION_EXPIRED(3107, "User session expired"),
|
||||
USER_TOKEN_INVALID(3108, "User token invalid"),
|
||||
USER_TOKEN_EXPIRED(3109, "User token expired(Token"),
|
||||
USER_REFRESH_TOKEN_INVALID(3110, "User refresh token invalid"),
|
||||
// USER_REFRESH_TOKEN_EXPIRED(3111, "User refresh token expired(Refresh Token"),
|
||||
USER_LOGOUT_SUCCESS(3112, "loignout success"),
|
||||
|
||||
// 用户信息相关
|
||||
USER_PROFILE_NOT_FOUND(3200, "User profile not found"),
|
||||
USER_UPDATE_PROFILE_SUCCESS(3201, "User profile updated"),
|
||||
USER_UPDATE_PROFILE_FAILED(3202, "User profile update failed"),
|
||||
USER_CHANGE_PASSWORD_SUCCESS(3203, "Change password success"),
|
||||
USER_CHANGE_PASSWORD_FAILED(3204, "Change password failed"),
|
||||
USER_OLD_PASSWORD_MISMATCH(3205, "Old password mismatch"),
|
||||
|
||||
// 权限相关 (如果你的用户模块包含复杂权限)
|
||||
// USER_PERMISSION_DENIED(3300, "用户权限不足(细粒度)"), // 可用于补充 FORBIDDEN
|
||||
|
||||
;
|
||||
|
||||
private final int code;
|
||||
private final String message;
|
||||
|
||||
ResultCode(int code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public String getMessage(String customDetail) {
|
||||
return this.message + (customDetail == null || customDetail.isEmpty() ? "" : " (" + customDetail + ")");
|
||||
}
|
||||
}
|
@ -10,9 +10,8 @@ public class CorsConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/api/**") // 允许 /api/ 下的所有请求
|
||||
.allowedOrigins("http://192.168.1.50:5173") // 允许来自该域的请求
|
||||
.allowedOrigins("*") // 允许来自该域的请求
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的 HTTP 方法
|
||||
.allowedHeaders("*") // 允许所有头部
|
||||
.allowCredentials(true); // 允许发送 Cookie
|
||||
.allowedHeaders("*"); // 允许所有头部
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package co.jp.app.config.security;
|
||||
|
||||
import co.jp.app.config.security.filter.JwtAuthenticationFilter;
|
||||
import co.jp.app.service.UserService;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
@ -21,11 +20,11 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
|
||||
@Configuration
|
||||
public class SecurityConfig {
|
||||
|
||||
//private final JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
private final JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||
private final UserDetailsService userDetailsService;
|
||||
|
||||
public SecurityConfig(@Lazy JwtAuthenticationFilter jwtAuthenticationFilter, @Lazy UserDetailsService userDetailsService) {
|
||||
//this.jwtAuthenticationFilter = jwtAuthenticationFilter;
|
||||
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
@ -47,29 +46,19 @@ public class SecurityConfig {
|
||||
return authenticationConfiguration.getAuthenticationManager();
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
// http.csrf(AbstractHttpConfigurer::disable)
|
||||
// .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
// .authorizeHttpRequests(auth -> auth
|
||||
// .requestMatchers("/api/user/login", "/api/user/register").permitAll()
|
||||
// .anyRequest().authenticated()
|
||||
// )
|
||||
// .authenticationProvider(authenticationProvider())
|
||||
// .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
//
|
||||
// return http.build();
|
||||
// }
|
||||
|
||||
// http config
|
||||
@Bean
|
||||
//暂时开放所有权限
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
|
||||
http.csrf(AbstractHttpConfigurer::disable)
|
||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.anyRequest().permitAll()
|
||||
);
|
||||
.requestMatchers("/api/user/login", "/api/user/register", "/api/inuhouse", "/api/dogs/pet").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.authenticationProvider(authenticationProvider())
|
||||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
|
||||
@Component
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
@ -36,14 +35,17 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
final String jwt;
|
||||
final String username;
|
||||
|
||||
//不需要token,直接返回Chain
|
||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
//透过token读取username
|
||||
jwt = authHeader.substring(7);
|
||||
username = jwtService.extractUsername(jwt);
|
||||
|
||||
//如果username为空且认证为空
|
||||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
|
||||
if (jwtService.isTokenValid(jwt, userDetails)) {
|
||||
@ -54,7 +56,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||
}
|
||||
}
|
||||
|
||||
//name非数字特殊符号,英文半角,密码规则大小写特殊,email符合格式
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
@ -3,21 +3,23 @@ package co.jp.app.controller;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import co.jp.app.entity.PetEntity;
|
||||
import co.jp.app.service.PetService;
|
||||
|
||||
@Controller
|
||||
@RestController
|
||||
public class DownloadController {
|
||||
@Autowired
|
||||
private PetService service;
|
||||
|
||||
@GetMapping("/api/dogs/pet")
|
||||
public String downloadById(@RequestParam List<Integer> id) {
|
||||
service.getPetByID(id);
|
||||
return "pet";
|
||||
@GetMapping("/download-image")
|
||||
public ResponseEntity<?> downloadById(@RequestParam List<Integer> id) {
|
||||
List<PetEntity> list = service.getPetByID(id);
|
||||
return ResponseEntity.ok(list);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,22 +3,25 @@ package co.jp.app.controller;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import co.jp.app.entity.PetEntity;
|
||||
import co.jp.app.service.PetService;
|
||||
|
||||
@Controller
|
||||
@RestController
|
||||
|
||||
public class PetController {
|
||||
|
||||
@Autowired
|
||||
private PetService service;
|
||||
|
||||
@GetMapping("/api/dogs/pet")
|
||||
public String getListByEntities(@RequestParam List<Integer> id) {
|
||||
service.getPetByID(id);
|
||||
return "pet";
|
||||
@GetMapping("/inuhouse")
|
||||
public ResponseEntity<?> getListByEntities(@RequestParam List<Integer> id) {
|
||||
|
||||
List<PetEntity> list = service.getPetByID(id);
|
||||
return ResponseEntity.ok(list);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -18,13 +19,13 @@ public class UploadController {
|
||||
@Autowired
|
||||
private UploadService service;
|
||||
|
||||
@PostMapping("/api/dogs/upload")
|
||||
public String upload() {
|
||||
@PostMapping("/upload")
|
||||
public ResponseEntity<?> upload() {
|
||||
List<PetEntity> list = new ArrayList<PetEntity>();
|
||||
|
||||
service.saveAllPets(list);
|
||||
List<PetEntity> pets = service.saveAllPets(list);
|
||||
|
||||
return "upload";
|
||||
return ResponseEntity.ok(pets);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,30 +1,29 @@
|
||||
package co.jp.app.controller;
|
||||
|
||||
import co.jp.app.common.ApiResponse;
|
||||
import co.jp.app.dto.LoginDto;
|
||||
import co.jp.app.dto.RegistrationDto;
|
||||
import co.jp.app.service.JwtService;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import co.jp.app.entity.ErrorEntity;
|
||||
import co.jp.app.entity.UserEntity;
|
||||
import co.jp.app.service.ErraService;
|
||||
import co.jp.app.service.UserService;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import co.jp.app.common.ApiResponse;
|
||||
import co.jp.app.dto.LoginDto;
|
||||
import co.jp.app.dto.RegistrationDto;
|
||||
import co.jp.app.dto.UserDto;
|
||||
import co.jp.app.entity.UserEntity;
|
||||
import co.jp.app.service.JwtService;
|
||||
import co.jp.app.service.UserService;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/user")
|
||||
public class UserController {
|
||||
|
||||
private final UserService userService;
|
||||
@ -37,41 +36,33 @@ public class UserController {
|
||||
this.jwtService = jwtService;
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public ResponseEntity<?> registerUser(@Valid @RequestBody RegistrationDto registrationDto) {
|
||||
try {
|
||||
@PostMapping("/api/user/register")
|
||||
public ResponseEntity<ApiResponse<UserDto>> registerUser(@Valid @RequestBody RegistrationDto registrationDto) {
|
||||
|
||||
UserEntity registeredUser = userService.registerNewUser(registrationDto);
|
||||
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(ApiResponse.success(registeredUser.getEmail()));
|
||||
} catch (Exception e) {
|
||||
UserDto userDto = new UserDto();
|
||||
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiResponse.fail("ユーザー登録失敗しました。"));
|
||||
}
|
||||
userDto.setEmail(registeredUser.getEmail());
|
||||
userDto.setName(registeredUser.getName());
|
||||
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(ApiResponse.success(userDto));
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginDto loginDto) {
|
||||
try {
|
||||
@PostMapping("/api/user/login")
|
||||
public ResponseEntity<ApiResponse<Map<String, String>>> authenticateUser(@Valid @RequestBody LoginDto loginDto) {
|
||||
|
||||
Authentication authentication = authenticationManager.authenticate(
|
||||
new UsernamePasswordAuthenticationToken(loginDto.getEmail(), loginDto.getPassword())
|
||||
);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
|
||||
|
||||
String jwtToken = jwtService.generateToken(userDetails); // 生成单一的Token
|
||||
String jwtToken = jwtService.generateToken(userDetails);
|
||||
|
||||
Map<String, String> tokenResponse = new HashMap<>();
|
||||
tokenResponse.put("token", jwtToken);
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(tokenResponse));
|
||||
|
||||
} catch (BadCredentialsException e) {
|
||||
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(ApiResponse.fail("メールアドレスまたはパスワードが間違っています。"));
|
||||
} catch (Exception e) {
|
||||
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.fail("サーバーエラー。"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,17 @@
|
||||
package co.jp.app.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class LoginDto {
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确,请输入有效的邮箱地址")
|
||||
private String email;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 30, message = "密码长度必须在6到30位之间")
|
||||
private String password;
|
||||
|
||||
public String getEmail() {
|
||||
|
@ -1,11 +1,26 @@
|
||||
package co.jp.app.dto;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class RegistrationDto {
|
||||
|
||||
//注:username可以为空
|
||||
@Size(min = 2, max = 20, message = "用户名长度必须在2到20个字符之间")
|
||||
@Pattern(
|
||||
regexp = "^[a-zA-Z\\p{script=Han}]+$",
|
||||
message = "用户名只能包含大小写英文字母和汉字,不能使用数字和标点符号"
|
||||
)
|
||||
private String name;
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
@Email(message = "邮箱格式不正确,请输入有效的邮箱地址")
|
||||
private String email;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 30, message = "密码长度必须在6到30位之间")
|
||||
private String password;
|
||||
|
||||
public String getName() {
|
||||
|
24
src/main/java/co/jp/app/dto/UserDto.java
Normal file
24
src/main/java/co/jp/app/dto/UserDto.java
Normal file
@ -0,0 +1,24 @@
|
||||
package co.jp.app.dto;
|
||||
|
||||
public class UserDto {
|
||||
|
||||
private String email;
|
||||
|
||||
private String name;
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "Pet")
|
||||
@Table(name = "Pet_Entity")
|
||||
public class PetEntity {
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
|
||||
|
38
src/main/java/co/jp/app/exception/BusinessException.java
Normal file
38
src/main/java/co/jp/app/exception/BusinessException.java
Normal file
@ -0,0 +1,38 @@
|
||||
package co.jp.app.exception;
|
||||
|
||||
import co.jp.app.common.ResultCode;
|
||||
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
private final ResultCode resultCode;
|
||||
private final String detailMessage; // 附加详细信息
|
||||
|
||||
public BusinessException(ResultCode resultCode) {
|
||||
super(resultCode.getMessage());
|
||||
this.resultCode = resultCode;
|
||||
this.detailMessage = null;
|
||||
}
|
||||
|
||||
// 有详细信息的构造函数
|
||||
public BusinessException(ResultCode resultCode, String detailMessage) {
|
||||
super(detailMessage != null && !detailMessage.isEmpty() ? detailMessage : resultCode.getMessage());
|
||||
this.resultCode = resultCode;
|
||||
this.detailMessage = detailMessage;
|
||||
}
|
||||
|
||||
// 无详细信息的构造函数
|
||||
public BusinessException(ResultCode resultCode, Throwable cause) {
|
||||
super(resultCode.getMessage(), cause);
|
||||
this.resultCode = resultCode;
|
||||
this.detailMessage = null;
|
||||
}
|
||||
|
||||
public ResultCode getResultCode() {
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
public String getDetailMessage() {
|
||||
return detailMessage;
|
||||
}
|
||||
|
||||
}
|
125
src/main/java/co/jp/app/exception/GlobalExceptionHandler.java
Normal file
125
src/main/java/co/jp/app/exception/GlobalExceptionHandler.java
Normal file
@ -0,0 +1,125 @@
|
||||
package co.jp.app.exception;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import co.jp.app.common.ApiResponse;
|
||||
import co.jp.app.common.ResultCode;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
// slf4j日志记录器
|
||||
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
// 业务异常
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ResponseEntity<ApiResponse<Object>> handleBusinessException(BusinessException ex) {
|
||||
logger.warn("业务异常: Code={}, Message={}, Detail={}", ex.getResultCode().getCode(), ex.getResultCode().getMessage(), ex.getDetailMessage(), ex);
|
||||
|
||||
ApiResponse<Object> body;
|
||||
|
||||
if (ex.getDetailMessage() != null && !ex.getDetailMessage().isEmpty()) {
|
||||
body = ApiResponse.fail(ex.getResultCode(), ex.getDetailMessage());
|
||||
} else {
|
||||
body = ApiResponse.fail(ex.getResultCode());
|
||||
}
|
||||
|
||||
HttpStatus httpStatus = determineHttpStatusFromResultCode(ex.getResultCode());
|
||||
return new ResponseEntity<>(body, httpStatus);
|
||||
}
|
||||
|
||||
// 参数校验异常
|
||||
@ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
|
||||
public ResponseEntity<ApiResponse<Object>> handleValidationExceptions(BindException ex) { // BindException 是 MethodArgumentNotValidException 的父类
|
||||
String errorMessage = ex.getBindingResult().getFieldErrors().stream()
|
||||
.map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
|
||||
.collect(Collectors.joining("; "));
|
||||
logger.warn("参数校验失败: {}", errorMessage, ex);
|
||||
return new ResponseEntity<>(ApiResponse.fail(ResultCode.VALIDATION_ERROR, errorMessage), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// 参数缺失异常
|
||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||
public ResponseEntity<ApiResponse<Object>> handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
|
||||
String message = "请求参数 '" + ex.getParameterName() + "' 不能为空";
|
||||
logger.warn(message, ex);
|
||||
return new ResponseEntity<>(ApiResponse.fail(ResultCode.BAD_REQUEST, message), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// Spring Security 用户认证异常
|
||||
@ExceptionHandler(AuthenticationException.class)
|
||||
public ResponseEntity<ApiResponse<Object>> handleAuthenticationException(AuthenticationException ex) {
|
||||
logger.warn("认证失败: {}", ex.getMessage(), ex);
|
||||
|
||||
if (ex instanceof BadCredentialsException) {
|
||||
|
||||
return new ResponseEntity<>(ApiResponse.fail(ResultCode.USER_INVALID_CREDENTIALS), HttpStatus.UNAUTHORIZED);
|
||||
} else if (ex instanceof UsernameNotFoundException) {
|
||||
|
||||
return new ResponseEntity<>(ApiResponse.fail(ResultCode.USER_INVALID_CREDENTIALS, "用户名或密码错误(用户不存在)"), HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
return new ResponseEntity<>(ApiResponse.fail(ResultCode.UNAUTHORIZED, ex.getMessage()), HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
// Spring Security 权限异常
|
||||
// @ExceptionHandler(AccessDeniedException.class)
|
||||
// public ResponseEntity<ApiResponse<Object>> handleAccessDeniedException(AccessDeniedException ex) {
|
||||
// logger.warn("权限不足: {}", ex.getMessage(), ex);
|
||||
// return new ResponseEntity<>(ApiResponse.fail(ResultCode.FORBIDDEN), HttpStatus.FORBIDDEN);
|
||||
// }
|
||||
|
||||
// 其他异常
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResponse<Object>> handleAllUncaughtException(Exception ex) {
|
||||
logger.error("发生未捕获的服务器内部错误!", ex);
|
||||
|
||||
return new ResponseEntity<>(ApiResponse.fail(ResultCode.INTERNAL_SERVER_ERROR), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
private HttpStatus determineHttpStatusFromResultCode(ResultCode resultCode) {
|
||||
if (resultCode == null) return HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
|
||||
switch (resultCode) {
|
||||
case SUCCESS:
|
||||
return HttpStatus.OK;
|
||||
case UNAUTHORIZED:
|
||||
case USER_INVALID_CREDENTIALS:
|
||||
case USER_TOKEN_INVALID:
|
||||
case USER_TOKEN_EXPIRED:
|
||||
return HttpStatus.UNAUTHORIZED;
|
||||
case FORBIDDEN:
|
||||
return HttpStatus.FORBIDDEN;
|
||||
case NOT_FOUND:
|
||||
case USER_ACCOUNT_NOT_FOUND:
|
||||
case USER_PROFILE_NOT_FOUND:
|
||||
return HttpStatus.NOT_FOUND;
|
||||
case CONFLICT:
|
||||
case USER_EMAIL_ALREADY_EXISTS:
|
||||
return HttpStatus.CONFLICT;
|
||||
case BAD_REQUEST:
|
||||
case VALIDATION_ERROR:
|
||||
case USER_PASSWORD_TOO_SHORT:
|
||||
return HttpStatus.BAD_REQUEST;
|
||||
default:
|
||||
if (resultCode.getCode() >= 2000 && resultCode.getCode() < 3000) {
|
||||
return HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
return HttpStatus.BAD_REQUEST;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,12 @@
|
||||
package co.jp.app.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import co.jp.app.entity.PetEntity;
|
||||
|
||||
@Repository
|
||||
public interface DownloadRepository extends JpaRepository<PetEntity, Integer>{
|
||||
|
||||
@Override
|
||||
default List<PetEntity> findAllById(Iterable<Integer> id) {
|
||||
return findAllById(id);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,15 +2,12 @@ package co.jp.app.repository;
|
||||
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import co.jp.app.entity.ErrorEntity;
|
||||
|
||||
|
||||
@Repository
|
||||
public interface ErrRepository extends JpaRepository<ErrorEntity, Integer>{
|
||||
|
||||
public default ErrorEntity getById(@Param("id") int id) {
|
||||
|
||||
return getById(id);
|
||||
}
|
||||
}
|
@ -1,15 +1,10 @@
|
||||
package co.jp.app.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import co.jp.app.entity.PetEntity;
|
||||
|
||||
@Repository
|
||||
public interface PetRepository extends JpaRepository<PetEntity, Integer> {
|
||||
|
||||
@Override
|
||||
default List<PetEntity> findAllById(Iterable<Integer> id) {
|
||||
return findAllById(id);
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,12 @@
|
||||
package co.jp.app.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import co.jp.app.entity.PetEntity;
|
||||
|
||||
@Repository
|
||||
public interface UploadRepository extends JpaRepository<PetEntity, Integer>{
|
||||
|
||||
@Override
|
||||
default <S extends PetEntity> List<S> saveAll(Iterable<S> entities) {
|
||||
// TODO 自動生成されたメソッド・スタブ
|
||||
return saveAll(entities);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
package co.jp.app.repository;
|
||||
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import java.util.Optional;
|
||||
|
||||
import co.jp.app.entity.UserEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
import co.jp.app.entity.UserEntity;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<UserEntity, Integer> {
|
||||
@ -15,4 +14,8 @@ public interface UserRepository extends JpaRepository<UserEntity, Integer> {
|
||||
boolean existsByEmail(String email);
|
||||
|
||||
Optional<UserEntity> findByEmail(String email);
|
||||
|
||||
boolean existsByName(String name);
|
||||
|
||||
Optional<UserEntity> findByName(String name);
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package co.jp.app.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import co.jp.app.entity.ErrorEntity;
|
||||
import co.jp.app.repository.ErrRepository;
|
||||
|
||||
|
||||
@Service
|
||||
public class ErraService {
|
||||
|
||||
@Autowired
|
||||
ErrRepository erraRepository;
|
||||
|
||||
public ErrorEntity getStatusById(int id) {
|
||||
return erraRepository.getById(id);
|
||||
}
|
||||
}
|
@ -3,9 +3,6 @@ package co.jp.app.service;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import co.jp.app.dto.RegistrationDto;
|
||||
import co.jp.app.entity.UserEntity;
|
||||
import co.jp.app.repository.UserRepository;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
@ -15,9 +12,14 @@ import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import co.jp.app.common.ResultCode;
|
||||
import co.jp.app.dto.RegistrationDto;
|
||||
import co.jp.app.entity.UserEntity;
|
||||
import co.jp.app.exception.BusinessException;
|
||||
import co.jp.app.repository.UserRepository;
|
||||
|
||||
@Service
|
||||
public class UserService implements UserDetailsService {
|
||||
|
||||
@ -29,11 +31,10 @@ public class UserService implements UserDetailsService {
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public UserEntity registerNewUser(@NotNull RegistrationDto registrationDto) throws Exception {
|
||||
public UserEntity registerNewUser(@NotNull RegistrationDto registrationDto) throws BusinessException {
|
||||
|
||||
if (userRepository.existsByEmail(registrationDto.getEmail())) {
|
||||
throw new Exception("error: Email" + registrationDto.getEmail() + " had been used");
|
||||
throw new BusinessException(ResultCode.USER_EMAIL_ALREADY_EXISTS,"error: Email" + registrationDto.getEmail() + " had been used");
|
||||
}
|
||||
|
||||
UserEntity newUser = new UserEntity();
|
||||
@ -50,7 +51,8 @@ public class UserService implements UserDetailsService {
|
||||
UserEntity userEntity = userRepository.findByEmail(email)
|
||||
.orElseThrow(() -> new UsernameNotFoundException(email + " not found"));
|
||||
|
||||
Collection<? extends GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
Collection<? extends GrantedAuthority> authorities = Collections
|
||||
.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
|
||||
return new User(
|
||||
userEntity.getEmail(),
|
||||
|
168
src/test/java/co/jp/app/UserServiceTest.java
Normal file
168
src/test/java/co/jp/app/UserServiceTest.java
Normal file
@ -0,0 +1,168 @@
|
||||
package co.jp.app;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.function.Executable;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import co.jp.app.common.ResultCode;
|
||||
import co.jp.app.dto.RegistrationDto;
|
||||
import co.jp.app.entity.UserEntity;
|
||||
import co.jp.app.exception.BusinessException;
|
||||
import co.jp.app.repository.UserRepository;
|
||||
import co.jp.app.service.UserService;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class UserServiceTest {
|
||||
|
||||
@Mock
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@InjectMocks
|
||||
private UserService userService;
|
||||
|
||||
private RegistrationDto registrationDto;
|
||||
private UserEntity userEntity;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
registrationDto = new RegistrationDto();
|
||||
registrationDto.setName("Test User");
|
||||
registrationDto.setEmail("test@example.com");
|
||||
registrationDto.setPassword("password123");
|
||||
|
||||
userEntity = new UserEntity();
|
||||
userEntity.setName("Test User");
|
||||
userEntity.setEmail("test@example.com");
|
||||
userEntity.setPassword("encodedPassword");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("新用户注册成功")
|
||||
public void registerNewUser_success() throws BusinessException {
|
||||
// Arrange
|
||||
when(userRepository.existsByEmail(anyString())).thenReturn(false);
|
||||
when(passwordEncoder.encode(anyString())).thenReturn("encodedPassword");
|
||||
when(userRepository.save(any(UserEntity.class))).thenReturn(userEntity);
|
||||
|
||||
// Act
|
||||
UserEntity savedUser = userService.registerNewUser(registrationDto);
|
||||
|
||||
// Assert
|
||||
assertNotNull(savedUser);
|
||||
assertEquals(userEntity.getEmail(), savedUser.getEmail());
|
||||
assertEquals("encodedPassword", savedUser.getPassword());
|
||||
verify(userRepository, times(1)).existsByEmail("test@example.com");
|
||||
verify(passwordEncoder, times(1)).encode("password123");
|
||||
verify(userRepository, times(1)).save(any(UserEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("用户注册 - 邮箱已存在")
|
||||
public void registerNewUser_emailAlreadyExists() {
|
||||
// Arrange
|
||||
when(userRepository.existsByEmail(anyString())).thenReturn(true);
|
||||
|
||||
// Act & Assert
|
||||
BusinessException exception = assertThrows(BusinessException.class, new Executable() {
|
||||
public void execute() throws Throwable {
|
||||
userService.registerNewUser(registrationDto);
|
||||
}
|
||||
});
|
||||
|
||||
assertEquals(ResultCode.USER_EMAIL_ALREADY_EXISTS, exception.getResultCode());
|
||||
verify(userRepository, times(1)).existsByEmail("test@example.com");
|
||||
verify(passwordEncoder, never()).encode(anyString());
|
||||
verify(userRepository, never()).save(any(UserEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("用户注册 - 密码过短")
|
||||
public void registerNewUser_passwordTooShort() {
|
||||
// Arrange
|
||||
registrationDto.setPassword("123"); // 设置一个短密码
|
||||
when(userRepository.existsByEmail(anyString())).thenReturn(false);
|
||||
|
||||
// Act & Assert
|
||||
BusinessException exception = assertThrows(BusinessException.class, new Executable() {
|
||||
public void execute() throws Throwable {
|
||||
userService.registerNewUser(registrationDto);
|
||||
}
|
||||
});
|
||||
|
||||
assertEquals(ResultCode.USER_PASSWORD_TOO_SHORT, exception.getResultCode());
|
||||
verify(userRepository, times(1)).existsByEmail("test@example.com");
|
||||
verify(passwordEncoder, never()).encode(anyString());
|
||||
verify(userRepository, never()).save(any(UserEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("用户注册 - 密码为null")
|
||||
public void registerNewUser_passwordIsNull() {
|
||||
// Arrange
|
||||
registrationDto.setPassword(null); // 设置密码为null
|
||||
when(userRepository.existsByEmail(anyString())).thenReturn(false);
|
||||
|
||||
// Act & Assert
|
||||
BusinessException exception = assertThrows(BusinessException.class, new Executable() {
|
||||
public void execute() throws Throwable {
|
||||
userService.registerNewUser(registrationDto);
|
||||
}
|
||||
});
|
||||
|
||||
assertEquals(ResultCode.USER_PASSWORD_TOO_SHORT, exception.getResultCode());
|
||||
verify(userRepository, times(1)).existsByEmail("test@example.com");
|
||||
verify(passwordEncoder, never()).encode(anyString());
|
||||
verify(userRepository, never()).save(any(UserEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("通过邮箱加载用户 - 用户存在")
|
||||
public void loadUserByUsername_userFound() {
|
||||
// Arrange
|
||||
when(userRepository.findByEmail(anyString())).thenReturn(Optional.of(userEntity));
|
||||
|
||||
// Act
|
||||
UserDetails userDetails = userService.loadUserByUsername("test@example.com");
|
||||
|
||||
// Assert
|
||||
assertNotNull(userDetails);
|
||||
assertEquals(userEntity.getEmail(), userDetails.getUsername());
|
||||
assertEquals(userEntity.getPassword(), userDetails.getPassword());
|
||||
assertTrue(userDetails.getAuthorities().stream()
|
||||
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals("ROLE_USER")));
|
||||
verify(userRepository, times(1)).findByEmail("test@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("通过邮箱加载用户 - 用户不存在")
|
||||
public void loadUserByUsername_userNotFound() {
|
||||
// Arrange
|
||||
when(userRepository.findByEmail(anyString())).thenReturn(Optional.empty());
|
||||
|
||||
// Act & Assert
|
||||
UsernameNotFoundException exception = assertThrows(UsernameNotFoundException.class, new Executable() {
|
||||
public void execute() throws Throwable {
|
||||
userService.loadUserByUsername("unknown@example.com");
|
||||
}
|
||||
});
|
||||
|
||||
assertEquals("unknown@example.com not found", exception.getMessage());
|
||||
verify(userRepository, times(1)).findByEmail("unknown@example.com");
|
||||
}
|
||||
}
|
70
src/test/java/co/jp/app/UserServiceTest1.java
Normal file
70
src/test/java/co/jp/app/UserServiceTest1.java
Normal file
@ -0,0 +1,70 @@
|
||||
package co.jp.app;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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 co.jp.app.dto.RegistrationDto;
|
||||
import co.jp.app.entity.UserEntity;
|
||||
import co.jp.app.exception.BusinessException;
|
||||
import co.jp.app.repository.UserRepository;
|
||||
import co.jp.app.service.UserService;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class UserServiceTest1 {
|
||||
|
||||
@Mock
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@InjectMocks
|
||||
private UserService userService;
|
||||
|
||||
private RegistrationDto registrationDto;
|
||||
private UserEntity userEntity;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
registrationDto = new RegistrationDto();
|
||||
registrationDto.setName("Test User");
|
||||
registrationDto.setEmail("test@example.com");
|
||||
registrationDto.setPassword("password123");
|
||||
|
||||
userEntity = new UserEntity();
|
||||
userEntity.setName("Test User");
|
||||
userEntity.setEmail("test@example.com");
|
||||
userEntity.setPassword("encodedPassword");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("新用户注册成功")
|
||||
public void registerNewUser_success() throws BusinessException {
|
||||
// Arrange
|
||||
when(userRepository.existsByEmail(anyString())).thenReturn(false);
|
||||
when(passwordEncoder.encode(anyString())).thenReturn("encodedPassword");
|
||||
when(userRepository.save(any(UserEntity.class))).thenReturn(userEntity);
|
||||
|
||||
// Act
|
||||
UserEntity savedUser = userService.registerNewUser(registrationDto);
|
||||
|
||||
// Assert
|
||||
assertNotNull(savedUser);
|
||||
assertEquals(userEntity.getEmail(), savedUser.getEmail());
|
||||
assertEquals("encodedPassword", savedUser.getPassword());
|
||||
verify(userRepository, times(1)).existsByEmail("test@example.com");
|
||||
verify(passwordEncoder, times(1)).encode("password123");
|
||||
verify(userRepository, times(1)).save(any(UserEntity.class));
|
||||
}
|
||||
|
||||
}
|
74
src/test/java/co/jp/app/dogtestbyadmin/UserServiceTest.java
Normal file
74
src/test/java/co/jp/app/dogtestbyadmin/UserServiceTest.java
Normal file
@ -0,0 +1,74 @@
|
||||
package co.jp.app.dogtestbyadmin;
|
||||
|
||||
import co.jp.app.dto.LoginDto;
|
||||
import co.jp.app.dto.RegistrationDto;
|
||||
import co.jp.app.dto.UserDto;
|
||||
import co.jp.app.entity.UserEntity;
|
||||
import co.jp.app.repository.UserRepository;
|
||||
import co.jp.app.service.UserService;
|
||||
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.boot.test.context.SpringBootTest;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SpringBootTest
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class UserServiceTest {
|
||||
|
||||
@Mock
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@InjectMocks
|
||||
private UserService userService;
|
||||
|
||||
private RegistrationDto registrationDto;
|
||||
private LoginDto loginDto;
|
||||
private UserDto userDto;
|
||||
private UserEntity userEntity;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
registrationDto = new RegistrationDto();
|
||||
registrationDto.setEmail("<EMAIL>");
|
||||
registrationDto.setName("test");
|
||||
registrationDto.setPassword("<PASSWORD>");
|
||||
|
||||
loginDto = new LoginDto();
|
||||
loginDto.setEmail("<EMAIL>");
|
||||
loginDto.setPassword("<PASSWORD>");
|
||||
|
||||
userDto = new UserDto();
|
||||
userDto.setEmail("<EMAIL>");
|
||||
userDto.setName("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegisterNewUser_normal () throws Exception{
|
||||
|
||||
when(userRepository.existsByEmail(registrationDto.getEmail())).thenReturn(false);
|
||||
|
||||
when(passwordEncoder.encode(registrationDto.getPassword())).thenReturn(registrationDto.getPassword());
|
||||
|
||||
when(userRepository.save(userEntity)).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegisterNewUser() throws Exception{
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void TestLoadUserByUsername() throws Exception{
|
||||
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#Generated by Maven Integration for Eclipse
|
||||
#Mon May 12 14:19:16 JST 2025
|
||||
m2e.projectLocation=C\:\\Users\\ichbi\\OneDrive\\\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7\\dog-1
|
||||
m2e.projectName=dog-1
|
||||
groupId=co.jp.app
|
||||
artifactId=dog-2
|
||||
version=0.0.1-SNAPSHOT
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/test-classes/co/jp/app/UserServiceTest$1.class
Normal file
BIN
target/test-classes/co/jp/app/UserServiceTest$1.class
Normal file
Binary file not shown.
BIN
target/test-classes/co/jp/app/UserServiceTest$2.class
Normal file
BIN
target/test-classes/co/jp/app/UserServiceTest$2.class
Normal file
Binary file not shown.
BIN
target/test-classes/co/jp/app/UserServiceTest$3.class
Normal file
BIN
target/test-classes/co/jp/app/UserServiceTest$3.class
Normal file
Binary file not shown.
BIN
target/test-classes/co/jp/app/UserServiceTest$4.class
Normal file
BIN
target/test-classes/co/jp/app/UserServiceTest$4.class
Normal file
Binary file not shown.
BIN
target/test-classes/co/jp/app/UserServiceTest.class
Normal file
BIN
target/test-classes/co/jp/app/UserServiceTest.class
Normal file
Binary file not shown.
BIN
target/test-classes/co/jp/app/UserServiceTest1.class
Normal file
BIN
target/test-classes/co/jp/app/UserServiceTest1.class
Normal file
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user