diff --git a/src/main/java/com/example/mutsideout_mju/controller/UserController.java b/src/main/java/com/example/mutsideout_mju/controller/UserController.java index 7907fbb..6e9596c 100644 --- a/src/main/java/com/example/mutsideout_mju/controller/UserController.java +++ b/src/main/java/com/example/mutsideout_mju/controller/UserController.java @@ -1,16 +1,17 @@ package com.example.mutsideout_mju.controller; import com.example.mutsideout_mju.authentication.AuthenticatedUser; +import com.example.mutsideout_mju.dto.request.user.DeleteUserDto; +import com.example.mutsideout_mju.dto.request.user.UpdateUserDto; import com.example.mutsideout_mju.dto.response.ResponseDto; import com.example.mutsideout_mju.dto.response.user.UserGradeResponseDto; import com.example.mutsideout_mju.entity.User; import com.example.mutsideout_mju.service.UserService; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor @@ -23,4 +24,18 @@ public ResponseEntity> getUserGrade(@Authentic UserGradeResponseDto userGradeResponseDto = userService.getUserGrade(user); return new ResponseEntity<>(ResponseDto.res(HttpStatus.OK, "유저 등급 조회 완료", userGradeResponseDto), HttpStatus.OK); } + + @PatchMapping + public ResponseEntity> updateUser(@AuthenticatedUser User user, + @RequestBody @Valid UpdateUserDto updateUserDto) { + userService.updateUser(user, updateUserDto); + return new ResponseEntity<>(ResponseDto.res(HttpStatus.OK, "유저 정보 수정 완료"), HttpStatus.OK); + } + + @DeleteMapping + public ResponseEntity> deleteUser(@AuthenticatedUser User user, + @RequestBody @Valid DeleteUserDto deleteUserDto) { + userService.deleteUser(user, deleteUserDto); + return new ResponseEntity<>(ResponseDto.res(HttpStatus.OK, "회원 탈퇴 성공"), HttpStatus.OK); + } } diff --git a/src/main/java/com/example/mutsideout_mju/dto/request/user/DeleteUserDto.java b/src/main/java/com/example/mutsideout_mju/dto/request/user/DeleteUserDto.java new file mode 100644 index 0000000..6c5aa40 --- /dev/null +++ b/src/main/java/com/example/mutsideout_mju/dto/request/user/DeleteUserDto.java @@ -0,0 +1,14 @@ +package com.example.mutsideout_mju.dto.request.user; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Getter; + +@Getter +public class DeleteUserDto { + @NotBlank(message = "비밀번호를 입력해주세요.") + @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*()])[A-Za-z\\d!@#$%^&*()]{8,20}$", message = "비밀번호는 영문과 숫자,특수기호를 조합하여 8~20글자 미만으로 입력해주세요.") + @Size(min = 8, max = 20, message = "비밀번호는 최소 8자에서 최대 20자까지 입력 가능합니다.") + private String password; +} diff --git a/src/main/java/com/example/mutsideout_mju/dto/request/user/UpdateUserDto.java b/src/main/java/com/example/mutsideout_mju/dto/request/user/UpdateUserDto.java new file mode 100644 index 0000000..acd00c2 --- /dev/null +++ b/src/main/java/com/example/mutsideout_mju/dto/request/user/UpdateUserDto.java @@ -0,0 +1,21 @@ +package com.example.mutsideout_mju.dto.request.user; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Getter; + +@Getter +public class UpdateUserDto { + @Size(min = 1, max = 10, message = "이름은 최소 1자에서 최대 10자까지 입력 가능합니다.") + private String newName; + + @NotBlank(message = "기존 비밀번호를 입력해주세요.") + @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*()])[A-Za-z\\d!@#$%^&*()]{8,20}$", message = "비밀번호는 영문과 숫자,특수기호를 조합하여 8~20글자 미만으로 입력해주세요.") + @Size(min = 8, max = 20, message = "비밀번호는 최소 8자에서 최대 20자까지 입력 가능합니다.") + private String originPassword; + + @Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[!@#$%^&*()])[A-Za-z\\d!@#$%^&*()]{8,20}$", message = "비밀번호는 영문과 숫자,특수기호를 조합하여 8~20글자 미만으로 입력해주세요.") + @Size(min = 8, max = 20, message = "비밀번호는 최소 8자에서 최대 20자까지 입력 가능합니다.") + private String newPassword; +} diff --git a/src/main/java/com/example/mutsideout_mju/entity/User.java b/src/main/java/com/example/mutsideout_mju/entity/User.java index b8da770..880a008 100644 --- a/src/main/java/com/example/mutsideout_mju/entity/User.java +++ b/src/main/java/com/example/mutsideout_mju/entity/User.java @@ -40,6 +40,14 @@ public void setUserGrade(Grade userGrade) { this.userGrade = userGrade; } + public void setName(String name) { + this.name = name; + } + + public void setPassword(String password) { + this.password = password; + } + public Grade determineGrade(long count) { if (count >= 4) { return Grade.TOMATO; diff --git a/src/main/java/com/example/mutsideout_mju/service/UserService.java b/src/main/java/com/example/mutsideout_mju/service/UserService.java index 05ad3e3..db86a02 100644 --- a/src/main/java/com/example/mutsideout_mju/service/UserService.java +++ b/src/main/java/com/example/mutsideout_mju/service/UserService.java @@ -1,10 +1,16 @@ package com.example.mutsideout_mju.service; +import com.example.mutsideout_mju.authentication.PasswordHashEncryption; +import com.example.mutsideout_mju.dto.request.user.DeleteUserDto; +import com.example.mutsideout_mju.dto.request.user.UpdateUserDto; import com.example.mutsideout_mju.dto.response.user.UserGradeResponseDto; import com.example.mutsideout_mju.entity.Grade; import com.example.mutsideout_mju.entity.SurveyOption; import com.example.mutsideout_mju.entity.User; import com.example.mutsideout_mju.entity.UserSurvey; +import com.example.mutsideout_mju.exception.ConflictException; +import com.example.mutsideout_mju.exception.ForbiddenException; +import com.example.mutsideout_mju.exception.errorCode.ErrorCode; import com.example.mutsideout_mju.repository.UserRepository; import com.example.mutsideout_mju.repository.UserSurveyRepository; import lombok.RequiredArgsConstructor; @@ -18,6 +24,7 @@ public class UserService { private final UserRepository userRepository; private final UserSurveyRepository userSurveyRepository; + private final PasswordHashEncryption passwordHashEncryption; @Transactional public UserGradeResponseDto calculateUserGrade(User user) { @@ -45,4 +52,34 @@ public static boolean isValidSurveyOption(UserSurvey userSurvey) { return (questionNumber >= 1 && questionNumber <= 3 && (option == SurveyOption.NORMAL || option == SurveyOption.YES)) || (questionNumber >= 4 && questionNumber <= 6 && option == SurveyOption.YES); } + + //회원 탈퇴 + public void deleteUser(User user, DeleteUserDto deleteUserDto) { + validatePassword(deleteUserDto.getPassword(), user.getPassword()); + userRepository.delete(user); + } + + //회원 정보 수정 + @Transactional + public void updateUser(User user, UpdateUserDto updateUserDto) { + validatePassword(updateUserDto.getOriginPassword(), user.getPassword()); + if (updateUserDto.getNewName() != null && !updateUserDto.getNewName().isEmpty()) { + //중복된 이름이 있을 경우 + if (userRepository.findByName(updateUserDto.getNewName()).isPresent()) { + throw new ConflictException(ErrorCode.DUPLICATED_NAME); + } + user.setName(updateUserDto.getNewName()); + } + if (updateUserDto.getNewPassword() != null && !updateUserDto.getNewPassword().isEmpty()) { + user.setPassword(passwordHashEncryption.encrypt(updateUserDto.getNewPassword())); + } + userRepository.save(user); + } + + //비밀번호 일치 여부 확인 + public void validatePassword(String plainPassword, String hashedPassword) { + if (!passwordHashEncryption.matches(plainPassword, hashedPassword)) { + throw new ForbiddenException(ErrorCode.NO_ACCESS, "비밀번호 정보가 일치하지 않습니다."); + } + } }