diff --git a/server/src/main/java/com/ticketing/server/global/exception/ErrorCode.java b/server/src/main/java/com/ticketing/server/global/exception/ErrorCode.java index 279a8ea..a666b57 100644 --- a/server/src/main/java/com/ticketing/server/global/exception/ErrorCode.java +++ b/server/src/main/java/com/ticketing/server/global/exception/ErrorCode.java @@ -17,6 +17,7 @@ public enum ErrorCode { MISMATCH_PASSWORD(BAD_REQUEST, "비밀번호가 일치하지 않습니다."), TOKEN_TYPE(BAD_REQUEST, "토큰 타입이 올바르지 않습니다."), UNAVAILABLE_REFRESH_TOKEN(BAD_REQUEST, "사용할 수 없는 토큰 입니다."), + UNABLE_CHANGE_GRADE(BAD_REQUEST, "동일한 등급으로 변경할 수 없습니다."), /* 403 FORBIDDEN : 접근 권한 제한 */ VALID_USER_ID(FORBIDDEN, "해당 정보에 접근 권한이 존재하지 않습니다."), @@ -50,6 +51,10 @@ public enum ErrorCode { throw new TicketingException(UNAVAILABLE_REFRESH_TOKEN); } + public static TicketingException throwUnableChangeGrade() { + throw new TicketingException(UNABLE_CHANGE_GRADE); + } + /* 403 FORBIDDEN : 접근 권한 제한 */ public static TicketingException throwValidUserId() { throw new TicketingException(VALID_USER_ID); diff --git a/server/src/main/java/com/ticketing/server/user/application/UserChangeGradeResponse.java b/server/src/main/java/com/ticketing/server/user/application/UserChangeGradeResponse.java new file mode 100644 index 0000000..93b5878 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/user/application/UserChangeGradeResponse.java @@ -0,0 +1,17 @@ +package com.ticketing.server.user.application; + +import com.ticketing.server.user.domain.UserGrade; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class UserChangeGradeResponse { + + private String email; + + private UserGrade beforeGrade; + + private UserGrade afterGrade; + +} diff --git a/server/src/main/java/com/ticketing/server/user/application/UserController.java b/server/src/main/java/com/ticketing/server/user/application/UserController.java index 46edf5b..24af054 100644 --- a/server/src/main/java/com/ticketing/server/user/application/UserController.java +++ b/server/src/main/java/com/ticketing/server/user/application/UserController.java @@ -1,6 +1,10 @@ package com.ticketing.server.user.application; +import static com.ticketing.server.user.domain.UserGrade.ROLES.ADMIN; +import static com.ticketing.server.user.domain.UserGrade.ROLES.USER; + import com.ticketing.server.user.application.request.SignUpRequest; +import com.ticketing.server.user.application.request.UserChangeGradeRequest; import com.ticketing.server.user.application.request.UserChangePasswordRequest; import com.ticketing.server.user.application.request.UserDeleteRequest; import com.ticketing.server.user.application.response.PaymentsResponse; @@ -8,8 +12,8 @@ import com.ticketing.server.user.application.response.SignUpResponse; import com.ticketing.server.user.application.response.UserChangePasswordResponse; import com.ticketing.server.user.application.response.UserDeleteResponse; import com.ticketing.server.user.application.response.UserDetailResponse; +import com.ticketing.server.user.domain.ChangeGradeDTO; import com.ticketing.server.user.domain.User; -import com.ticketing.server.user.domain.UserGrade; import com.ticketing.server.user.service.dto.UserDetailDTO; import com.ticketing.server.user.service.interfaces.UserApisService; import com.ticketing.server.user.service.interfaces.UserService; @@ -47,21 +51,21 @@ public class UserController { } @GetMapping("/details") - @Secured(UserGrade.ROLES.USER) + @Secured(USER) public ResponseEntity details(@AuthenticationPrincipal UserDetails userRequest) { UserDetailDTO userDetail = userService.findDetailByEmail(userRequest.getUsername()); return ResponseEntity.status(HttpStatus.OK).body(userDetail.toResponse()); } @DeleteMapping - @Secured(UserGrade.ROLES.USER) + @Secured(USER) public ResponseEntity deleteUser(@RequestBody @Valid UserDeleteRequest request) { User user = userService.delete(request.toDeleteUserDto(passwordEncoder)); return ResponseEntity.status(HttpStatus.OK).body(UserDeleteResponse.from(user)); } @PutMapping("/password") - @Secured(UserGrade.ROLES.USER) + @Secured(USER) public ResponseEntity changePassword( @AuthenticationPrincipal UserDetails userRequest, @RequestBody @Valid UserChangePasswordRequest request) { @@ -69,8 +73,15 @@ public class UserController { return ResponseEntity.status(HttpStatus.OK).body(UserChangePasswordResponse.from(user)); } + @PostMapping("/grade") + @Secured(ADMIN) + public ResponseEntity changeGrade(@RequestBody @Valid UserChangeGradeRequest request) { + ChangeGradeDTO changeGradeDto = userService.changeGrade(request.getEmail(), request.getAfterGrade()); + return ResponseEntity.status(HttpStatus.OK).body(changeGradeDto.toResponse()); + } + @GetMapping("/payments") - @Secured(UserGrade.ROLES.USER) + @Secured(USER) public ResponseEntity getPayments(@AuthenticationPrincipal UserDetails userRequest) { PaymentsResponse paymentDetails = userApisService.findPaymentsByEmail(userRequest.getUsername()); return ResponseEntity.status(HttpStatus.OK).body(paymentDetails); diff --git a/server/src/main/java/com/ticketing/server/user/application/request/UserChangeGradeRequest.java b/server/src/main/java/com/ticketing/server/user/application/request/UserChangeGradeRequest.java new file mode 100644 index 0000000..ba70360 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/user/application/request/UserChangeGradeRequest.java @@ -0,0 +1,19 @@ +package com.ticketing.server.user.application.request; + +import com.ticketing.server.user.domain.UserGrade; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import lombok.Getter; + +@Getter +public class UserChangeGradeRequest { + + @NotEmpty(message = "{validation.not.empty.email}") + @Email(message = "{validation.email}") + private String email; + + @NotNull(message = "{validation.not.null.grade}") + private UserGrade afterGrade; + +} diff --git a/server/src/main/java/com/ticketing/server/user/domain/ChangeGradeDTO.java b/server/src/main/java/com/ticketing/server/user/domain/ChangeGradeDTO.java new file mode 100644 index 0000000..249b782 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/user/domain/ChangeGradeDTO.java @@ -0,0 +1,19 @@ +package com.ticketing.server.user.domain; + +import com.ticketing.server.user.application.UserChangeGradeResponse; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class ChangeGradeDTO { + + private final String email; + private final UserGrade beforeGrade; + private final UserGrade afterGrade; + + public UserChangeGradeResponse toResponse() { + return new UserChangeGradeResponse(email, beforeGrade, afterGrade); + } + +} diff --git a/server/src/main/java/com/ticketing/server/user/domain/User.java b/server/src/main/java/com/ticketing/server/user/domain/User.java index 44c4732..7d58eee 100644 --- a/server/src/main/java/com/ticketing/server/user/domain/User.java +++ b/server/src/main/java/com/ticketing/server/user/domain/User.java @@ -93,4 +93,14 @@ public class User extends AbstractEntity { } } + public ChangeGradeDTO changeGrade(UserGrade afterGrade) { + if (grade.equals(afterGrade)) { + throw ErrorCode.throwUnableChangeGrade(); + } + final UserGrade beforeGrade = this.grade; + + this.grade = afterGrade; + return new ChangeGradeDTO(email, beforeGrade, afterGrade); + } + } diff --git a/server/src/main/java/com/ticketing/server/user/service/UserServiceImpl.java b/server/src/main/java/com/ticketing/server/user/service/UserServiceImpl.java index 39b8a98..ebf3ae5 100644 --- a/server/src/main/java/com/ticketing/server/user/service/UserServiceImpl.java +++ b/server/src/main/java/com/ticketing/server/user/service/UserServiceImpl.java @@ -1,8 +1,10 @@ package com.ticketing.server.user.service; import com.ticketing.server.global.exception.ErrorCode; +import com.ticketing.server.user.domain.ChangeGradeDTO; import com.ticketing.server.user.domain.SequenceGenerator; import com.ticketing.server.user.domain.User; +import com.ticketing.server.user.domain.UserGrade; import com.ticketing.server.user.domain.repository.UserRepository; import com.ticketing.server.user.service.dto.ChangePasswordDTO; import com.ticketing.server.user.service.dto.DeleteUserDTO; @@ -53,6 +55,13 @@ public class UserServiceImpl implements UserService { return user.changePassword(changePasswordDto); } + @Override + @Transactional + public ChangeGradeDTO changeGrade(@NotNull String email, @NotNull UserGrade grade) { + User user = findNotDeletedUserByEmail(email); + return user.changeGrade(grade); + } + @Override public UserDetailDTO findDetailByEmail(@NotNull String email) { User user = userRepository.findByEmail(email) diff --git a/server/src/main/java/com/ticketing/server/user/service/interfaces/UserService.java b/server/src/main/java/com/ticketing/server/user/service/interfaces/UserService.java index adc998f..ec5b313 100644 --- a/server/src/main/java/com/ticketing/server/user/service/interfaces/UserService.java +++ b/server/src/main/java/com/ticketing/server/user/service/interfaces/UserService.java @@ -1,12 +1,15 @@ package com.ticketing.server.user.service.interfaces; +import com.ticketing.server.user.domain.ChangeGradeDTO; import com.ticketing.server.user.domain.User; +import com.ticketing.server.user.domain.UserGrade; import com.ticketing.server.user.service.dto.ChangePasswordDTO; import com.ticketing.server.user.service.dto.DeleteUserDTO; import com.ticketing.server.user.service.dto.SignUpDTO; import com.ticketing.server.user.service.dto.UserDetailDTO; import javax.validation.Valid; import javax.validation.constraints.NotNull; +import org.springframework.transaction.annotation.Transactional; public interface UserService { @@ -16,6 +19,9 @@ public interface UserService { User changePassword(@Valid ChangePasswordDTO changePasswordDto); + @Transactional + ChangeGradeDTO changeGrade(@NotNull String email, @NotNull UserGrade grade); + UserDetailDTO findDetailByEmail(@NotNull String email); User findNotDeletedUserByEmail(@NotNull String email); diff --git a/server/src/test/java/com/ticketing/server/user/domain/UserTest.java b/server/src/test/java/com/ticketing/server/user/domain/UserTest.java index 221d691..bb7ee84 100644 --- a/server/src/test/java/com/ticketing/server/user/domain/UserTest.java +++ b/server/src/test/java/com/ticketing/server/user/domain/UserTest.java @@ -2,7 +2,9 @@ package com.ticketing.server.user.domain; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; +import com.ticketing.server.global.exception.ErrorCode; import com.ticketing.server.global.exception.TicketingException; import com.ticketing.server.user.service.dto.ChangePasswordDTO; import com.ticketing.server.user.service.dto.DeleteUserDTO; @@ -33,6 +35,37 @@ public class UserTest { users = provideCorrectUsers().collect(Collectors.toMap(User::getEmail, user -> user)); } + @Test + @DisplayName("동일한 권한으로 변경 시 예외처리") + void changeGradeFail() { + // given + User user = users.get("ticketing1@gmail.com"); + + // when + // then + assertThatThrownBy(() -> user.changeGrade(UserGrade.USER)) + .isInstanceOf(TicketingException.class) + .extracting("errorCode") + .isEqualTo(ErrorCode.UNABLE_CHANGE_GRADE); + } + + @Test + @DisplayName("권한 변경 성공") + void changeGradeSuccess() { + // given + User user = users.get("ticketing1@gmail.com"); + + // when + ChangeGradeDTO changeGradeDto = user.changeGrade(UserGrade.ADMIN); + + // then + assertAll( + () -> assertThat(changeGradeDto.getEmail()).isEqualTo("ticketing1@gmail.com"), + () -> assertThat(changeGradeDto.getBeforeGrade()).isEqualTo(UserGrade.USER), + () -> assertThat(changeGradeDto.getAfterGrade()).isEqualTo(UserGrade.ADMIN) + ); + } + @ParameterizedTest @MethodSource("provideDifferentPasswordDeleteUsers") @DisplayName("입력된 패스워드가 다를 경우")