Compare commits
47 Commits
34de018ef0
...
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 | |||
2617b4a5ae | |||
6d3f837d7c | |||
76102067ab | |||
38e91f45b6 | |||
b23ba3b92a | |||
10a51413d0 | |||
b113c43959 | |||
f6099d382f | |||
e307eb06cc | |||
9ea0721375 | |||
cacdef44d7 |
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
bin
Submodule
1
bin
Submodule
Submodule bin added at aa8d8275e8
@ -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;
|
||||
@ -47,12 +46,13 @@ public class SecurityConfig {
|
||||
return authenticationConfiguration.getAuthenticationManager();
|
||||
}
|
||||
|
||||
// http config
|
||||
@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()
|
||||
.requestMatchers("/api/user/login", "/api/user/register", "/api/inuhouse", "/api/dogs/pet").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.authenticationProvider(authenticationProvider())
|
||||
@ -60,4 +60,5 @@ public class SecurityConfig {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
25
src/main/java/co/jp/app/controller/DownloadController.java
Normal file
25
src/main/java/co/jp/app/controller/DownloadController.java
Normal file
@ -0,0 +1,25 @@
|
||||
package co.jp.app.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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;
|
||||
|
||||
@RestController
|
||||
public class DownloadController {
|
||||
@Autowired
|
||||
private PetService service;
|
||||
|
||||
@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,26 +4,28 @@ 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;
|
||||
|
||||
import co.jp.app.entity.PetEntity;
|
||||
import co.jp.app.service.UploadService;
|
||||
|
||||
@CrossOrigin("http://192.168.1.50:5173")
|
||||
@Controller
|
||||
public class UploadController {
|
||||
|
||||
@Autowired
|
||||
//private UploadService service;
|
||||
private UploadService service;
|
||||
|
||||
@PostMapping("/api/dogs/upload")
|
||||
public String upload() {
|
||||
@PostMapping("/upload")
|
||||
public ResponseEntity<?> upload() {
|
||||
List<PetEntity> list = new ArrayList<PetEntity>();
|
||||
|
||||
//service.saveAll(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);
|
||||
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 {
|
||||
Authentication authentication = authenticationManager.authenticate(
|
||||
new UsernamePasswordAuthenticationToken(loginDto.getEmail(), loginDto.getPassword())
|
||||
);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
|
||||
@PostMapping("/api/user/login")
|
||||
public ResponseEntity<ApiResponse<Map<String, String>>> authenticateUser(@Valid @RequestBody LoginDto loginDto) {
|
||||
|
||||
String jwtToken = jwtService.generateToken(userDetails); // 生成单一的Token
|
||||
Authentication authentication = authenticationManager.authenticate(
|
||||
new UsernamePasswordAuthenticationToken(loginDto.getEmail(), loginDto.getPassword())
|
||||
);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
|
||||
|
||||
Map<String, String> tokenResponse = new HashMap<>();
|
||||
tokenResponse.put("token", jwtToken);
|
||||
String jwtToken = jwtService.generateToken(userDetails);
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(tokenResponse));
|
||||
Map<String, String> tokenResponse = new HashMap<>();
|
||||
tokenResponse.put("token", jwtToken);
|
||||
|
||||
} catch (BadCredentialsException e) {
|
||||
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(ApiResponse.fail("メールアドレスまたはパスワードが間違っています。"));
|
||||
} catch (Exception e) {
|
||||
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ApiResponse.fail("サーバーエラー。"));
|
||||
}
|
||||
return ResponseEntity.ok(ApiResponse.success(tokenResponse));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
12
src/main/java/co/jp/app/repository/DownloadRepository.java
Normal file
12
src/main/java/co/jp/app/repository/DownloadRepository.java
Normal file
@ -0,0 +1,12 @@
|
||||
package co.jp.app.repository;
|
||||
|
||||
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>{
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
23
src/main/java/co/jp/app/service/DownloadService.java
Normal file
23
src/main/java/co/jp/app/service/DownloadService.java
Normal file
@ -0,0 +1,23 @@
|
||||
package co.jp.app.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import co.jp.app.entity.PetEntity;
|
||||
import co.jp.app.repository.DownloadRepository;
|
||||
|
||||
@Service
|
||||
public class DownloadService {
|
||||
|
||||
@Autowired
|
||||
private DownloadRepository downloadDao;
|
||||
|
||||
public List<PetEntity> getPetByID(Iterable<Integer> id) {
|
||||
|
||||
return downloadDao.findAllById(id);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
21
src/main/java/co/jp/app/service/UploadService.java
Normal file
21
src/main/java/co/jp/app/service/UploadService.java
Normal file
@ -0,0 +1,21 @@
|
||||
package co.jp.app.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import co.jp.app.entity.PetEntity;
|
||||
import co.jp.app.repository.UploadRepository;
|
||||
|
||||
@Service
|
||||
public class UploadService {
|
||||
|
||||
@Autowired
|
||||
private UploadRepository uploadDao;
|
||||
|
||||
public List<PetEntity> saveAllPets(List<PetEntity> entities) {
|
||||
return uploadDao.saveAll(entities);
|
||||
}
|
||||
|
||||
}
|
@ -2,11 +2,7 @@ package co.jp.app.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
@ -16,11 +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 co.jp.app.entity.PetEntity;
|
||||
import co.jp.app.repository.UploadRepository;
|
||||
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 {
|
||||
|
||||
@ -32,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("エラー:メール:" + registrationDto.getEmail() + " はすでに登録されました。");
|
||||
throw new BusinessException(ResultCode.USER_EMAIL_ALREADY_EXISTS,"error: Email" + registrationDto.getEmail() + " had been used");
|
||||
}
|
||||
|
||||
UserEntity newUser = new UserEntity();
|
||||
@ -53,16 +51,17 @@ 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")); // 示例:给所有用户一个ROLE_USER权限
|
||||
Collection<? extends GrantedAuthority> authorities = Collections
|
||||
.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
|
||||
return new User(
|
||||
userEntity.getEmail(),
|
||||
userEntity.getPassword(),
|
||||
true, // enabled
|
||||
true, // accountNonExpired
|
||||
true, // credentialsNonExpired
|
||||
true, // accountNonLocked
|
||||
authorities // 用户的权限集合
|
||||
true, // enabled
|
||||
true, // accountNonExpired
|
||||
true, // credentialsNonExpired
|
||||
true, // accountNonLocked
|
||||
authorities // role
|
||||
);
|
||||
}
|
||||
}
|
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
target/classes/.gitignore
vendored
1
target/classes/.gitignore
vendored
@ -1 +0,0 @@
|
||||
/co/
|
@ -1,7 +0,0 @@
|
||||
#Generated by Maven Integration for Eclipse
|
||||
#Mon May 12 11:38:52 JST 2025
|
||||
m2e.projectLocation=E\:\\jugyo\\asciimg-master\\Dog-1
|
||||
m2e.projectName=dog-1
|
||||
groupId=co.jp.app
|
||||
artifactId=dog-2
|
||||
version=0.0.1-SNAPSHOT
|
@ -10,4 +10,4 @@ spring.datasource.password=coder
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
|
||||
jwt.secret=wM7Pz4BxvZ5NcLaBpgJm0eRQ5ztc3W5+OPH0E7g3gcQ=
|
||||
jwt.token-expiration-ms==900000
|
||||
jwt.token-expiration-ms=900000
|
BIN
target/classes/co/jp/app/common/ApiResponse.class
Normal file
BIN
target/classes/co/jp/app/common/ApiResponse.class
Normal file
Binary file not shown.
BIN
target/classes/co/jp/app/config/CorsConfig.class
Normal file
BIN
target/classes/co/jp/app/config/CorsConfig.class
Normal file
Binary file not shown.
BIN
target/classes/co/jp/app/config/security/SecurityConfig.class
Normal file
BIN
target/classes/co/jp/app/config/security/SecurityConfig.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
target/classes/co/jp/app/controller/DownloadController.class
Normal file
BIN
target/classes/co/jp/app/controller/DownloadController.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
target/classes/co/jp/app/controller/UserController.class
Normal file
BIN
target/classes/co/jp/app/controller/UserController.class
Normal file
Binary file not shown.
BIN
target/classes/co/jp/app/dto/LoginDto.class
Normal file
BIN
target/classes/co/jp/app/dto/LoginDto.class
Normal file
Binary file not shown.
BIN
target/classes/co/jp/app/dto/RegistrationDto.class
Normal file
BIN
target/classes/co/jp/app/dto/RegistrationDto.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
target/classes/co/jp/app/repository/DownloadRepository.class
Normal file
BIN
target/classes/co/jp/app/repository/DownloadRepository.class
Normal file
Binary file not shown.
Binary file not shown.
BIN
target/classes/co/jp/app/service/DownloadService.class
Normal file
BIN
target/classes/co/jp/app/service/DownloadService.class
Normal file
Binary file not shown.
BIN
target/classes/co/jp/app/service/JwtService.class
Normal file
BIN
target/classes/co/jp/app/service/JwtService.class
Normal file
Binary file not shown.
BIN
target/classes/co/jp/app/service/UploadService.class
Normal file
BIN
target/classes/co/jp/app/service/UploadService.class
Normal file
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