From 66adc065e29e5786d9dd0ff1038fdf420ad79e55 Mon Sep 17 00:00:00 2001 From: hyejungg Date: Mon, 20 Sep 2021 23:34:58 +0900 Subject: [PATCH 1/8] =?UTF-8?q?[feat:#9]=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D,=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 이메일 등록 후 중복, 타입 일치하지 않은 경우 모두 확인 완료 --- .../backend/ticket/config/ErrorCode.java | 25 ++++++++ .../backend/ticket/config/ErrorResponse.java | 27 ++++++++ .../ticket/config/GlobalExceptionHandler.java | 64 +++++++++++++++++++ .../backend/ticket/config/SuccessCode.java | 17 +++++ .../ticket/config/SuccessResponse.java | 27 ++++++++ .../ticket/controller/UserController.java | 42 ++++++++++++ .../comento/backend/ticket/domain/User.java | 37 +++++++++++ .../comento/backend/ticket/dto/UserDto.java | 21 ++++++ .../ticket/repository/UserRepository.java | 13 ++++ .../backend/ticket/service/UserService.java | 42 ++++++++++++ ticket/src/main/resources/logback.xml | 29 +++++++++ .../ticket/repository/UserRepositryTest.java | 39 +++++++++++ .../ticket/service/UserServiceTest.java | 50 +++++++++++++++ 13 files changed, 433 insertions(+) create mode 100644 ticket/src/main/java/comento/backend/ticket/config/ErrorCode.java create mode 100644 ticket/src/main/java/comento/backend/ticket/config/ErrorResponse.java create mode 100644 ticket/src/main/java/comento/backend/ticket/config/GlobalExceptionHandler.java create mode 100644 ticket/src/main/java/comento/backend/ticket/config/SuccessCode.java create mode 100644 ticket/src/main/java/comento/backend/ticket/config/SuccessResponse.java create mode 100644 ticket/src/main/java/comento/backend/ticket/controller/UserController.java create mode 100644 ticket/src/main/java/comento/backend/ticket/domain/User.java create mode 100644 ticket/src/main/java/comento/backend/ticket/dto/UserDto.java create mode 100644 ticket/src/main/java/comento/backend/ticket/repository/UserRepository.java create mode 100644 ticket/src/main/java/comento/backend/ticket/service/UserService.java create mode 100644 ticket/src/main/resources/logback.xml create mode 100644 ticket/src/test/java/comento/backend/ticket/repository/UserRepositryTest.java create mode 100644 ticket/src/test/java/comento/backend/ticket/service/UserServiceTest.java diff --git a/ticket/src/main/java/comento/backend/ticket/config/ErrorCode.java b/ticket/src/main/java/comento/backend/ticket/config/ErrorCode.java new file mode 100644 index 0000000..c69b420 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/config/ErrorCode.java @@ -0,0 +1,25 @@ +package comento.backend.ticket.config; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ErrorCode { + /** + * @see : 예외케이스 정의 + */ + + INVALID_VALUE(400, HttpStatus.BAD_REQUEST, "BAD REQUEST", "잘못된 요청입니다. 다시 요청해주세요."), + NO_USER(401, HttpStatus.UNAUTHORIZED,"UNAUTHORIZED", "등록되지 않은 사용자입니다"), + NO_DATA(404, HttpStatus.NOT_FOUND, "NOT FOUND ERROR", "요청할 수 없는 리소스입니다."), + VALID_USER(409, HttpStatus.CONFLICT, "CONFLICT", "이미 존재하는 사용자입니다."), + VALID_SEAT(409, HttpStatus.CONFLICT, "CONFLICT", "이미 예약된 좌석입니다."), + SERVER_ERROR(500, HttpStatus.INTERNAL_SERVER_ERROR, "INTERVAL_SERVER ERROR", "서버 에러입니다."); + + private final Integer status; + private final HttpStatus httpStatus; + private final String message; + private final String reason; +} diff --git a/ticket/src/main/java/comento/backend/ticket/config/ErrorResponse.java b/ticket/src/main/java/comento/backend/ticket/config/ErrorResponse.java new file mode 100644 index 0000000..374c600 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/config/ErrorResponse.java @@ -0,0 +1,27 @@ +package comento.backend.ticket.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import org.springframework.http.HttpStatus; + +@Data +@AllArgsConstructor +@Builder +public class ErrorResponse { + private Integer status; + private String message; + private String reason; + private HttpStatus httpStatus; + + //공통된 Error Message를 전송할 때 사용 + public static ErrorResponse res(final Integer status, final String message, final String reason, final HttpStatus httpStatus){ + return ErrorResponse.builder() + .status(status) + .message(message) + .reason(reason) + .httpStatus(httpStatus) + .build(); + } +} + diff --git a/ticket/src/main/java/comento/backend/ticket/config/GlobalExceptionHandler.java b/ticket/src/main/java/comento/backend/ticket/config/GlobalExceptionHandler.java new file mode 100644 index 0000000..0090c0f --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/config/GlobalExceptionHandler.java @@ -0,0 +1,64 @@ +package comento.backend.ticket.config; + +import comento.backend.ticket.service.UserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; + +@RestControllerAdvice //@Conroller 전역에서 발생 가능한 예외를 잡아 처리 +@Slf4j +public class GlobalExceptionHandler { + private static ErrorCode errorCode; + + /** + * 특정 Exception을 지정하여 별도로 처리 + */ + + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public static ErrorResponse missMatchExceptionHandler(MethodArgumentTypeMismatchException e){ + errorCode = ErrorCode.INVALID_VALUE; + log.error(errorCode.getStatus() + errorCode.getMessage(), e); + return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); + } + + @ExceptionHandler(IllegalStateException.class) + public static ErrorResponse illegalExceptionHandler(IllegalStateException ie){ + errorCode = ErrorCode.SERVER_ERROR; + log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), ie); + return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); + } + + @ExceptionHandler(RuntimeException.class) + public static ErrorResponse exceptionHandler(RuntimeException e){ + switch(e.getMessage()){ + case "400" : + errorCode = ErrorCode.INVALID_VALUE; + log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e); + return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); + case "401" : + errorCode = ErrorCode.NO_USER; + log.error(errorCode.getStatus() + " " + errorCode.getMessage(), e); + return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); + case "404" : + errorCode = ErrorCode.NO_DATA; + log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e); + return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); + case "409" : + if (e.getClass().getSimpleName().equals("UserService")) { //호출된 곳이 UserService + errorCode = ErrorCode.VALID_USER; + log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e); + return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); + }else{ //좌석 예약 시 + errorCode = ErrorCode.VALID_SEAT; + log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e); + return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); + } + default : + errorCode = ErrorCode.SERVER_ERROR; + log.error(errorCode.getStatus() + errorCode.getMessage(), e); + return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); + } + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/config/SuccessCode.java b/ticket/src/main/java/comento/backend/ticket/config/SuccessCode.java new file mode 100644 index 0000000..61701c7 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/config/SuccessCode.java @@ -0,0 +1,17 @@ +package comento.backend.ticket.config; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum SuccessCode { + + OK(200, HttpStatus.OK, "OK"), + CREATED(201, HttpStatus.CREATED, "CREATED"); + + private final Integer status; + private final HttpStatus httpStatus; + private final String message; +} diff --git a/ticket/src/main/java/comento/backend/ticket/config/SuccessResponse.java b/ticket/src/main/java/comento/backend/ticket/config/SuccessResponse.java new file mode 100644 index 0000000..39aaf0d --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/config/SuccessResponse.java @@ -0,0 +1,27 @@ +package comento.backend.ticket.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import org.springframework.http.HttpStatus; + +@Data +@AllArgsConstructor +@Builder +public class SuccessResponse { + private Integer status; + private String message; + private Object data; + private HttpStatus httpStatus; + + //공통된 Success Message를 전송할 때 사용 + public static SuccessResponse res(final Integer status, final String message, final Object data, final HttpStatus httpStatus){ + return SuccessResponse.builder() + .status(status) + .message(message) + .data(data) + .httpStatus(httpStatus) + .build(); + } + +} diff --git a/ticket/src/main/java/comento/backend/ticket/controller/UserController.java b/ticket/src/main/java/comento/backend/ticket/controller/UserController.java new file mode 100644 index 0000000..b376312 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/controller/UserController.java @@ -0,0 +1,42 @@ +package comento.backend.ticket.controller; + +import comento.backend.ticket.config.SuccessCode; +import comento.backend.ticket.config.SuccessResponse; +import comento.backend.ticket.domain.User; +import comento.backend.ticket.dto.UserDto; +import comento.backend.ticket.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api") +public class UserController { + private SuccessCode successCode; + private final UserService userService; + private static final String CREATED_MSG = "등록 성공"; + + @Autowired + public UserController(UserService userService) { + this.userService = userService; + } + + //이메일 등록 + @PostMapping("/signup") + public SuccessResponse addEmail(@Validated @RequestBody String email){ + UserDto userDto = new UserDto(email); + System.out.println(userDto.toString()); + User receive = userService.saveUser(userDto); + + if(receive != null){ + successCode = SuccessCode.CREATED; + return SuccessResponse.res( + successCode.getStatus(), successCode.getMessage(), CREATED_MSG , successCode.getHttpStatus()); + }else{ + throw new RuntimeException("404"); + } + } + + +} diff --git a/ticket/src/main/java/comento/backend/ticket/domain/User.java b/ticket/src/main/java/comento/backend/ticket/domain/User.java new file mode 100644 index 0000000..ac79a47 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/domain/User.java @@ -0,0 +1,37 @@ +package comento.backend.ticket.domain; + +import lombok.Builder; +import lombok.Data; +import org.hibernate.annotations.CreationTimestamp; +import org.springframework.data.annotation.CreatedDate; + +import javax.persistence.*; +import java.util.Date; + +@Entity +@Data +@Table(name="User") +public class User{ + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="user_id") + private Long id; + + @Column(nullable = true) + private String email; + + @Column() + @CreationTimestamp //Entity가 생성되어 저장될 때 시간이 자동 저장 + @Temporal(TemporalType.TIMESTAMP) + private Date create_at; + + public User(){} + + @Builder + public User(long id, String email, Date create_at) { + this.id = id; + this.email = email; + this.create_at = create_at; + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/dto/UserDto.java b/ticket/src/main/java/comento/backend/ticket/dto/UserDto.java new file mode 100644 index 0000000..c45c80a --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/dto/UserDto.java @@ -0,0 +1,21 @@ +package comento.backend.ticket.dto; + +import comento.backend.ticket.domain.User; +import lombok.Data; +import lombok.Getter; + +@Getter +@Data +public class UserDto { + private String email; + + public UserDto(String email) { + this.email = email; + } + + public User toEntity(){ + return User.builder() + .email(email) + .build(); + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/repository/UserRepository.java b/ticket/src/main/java/comento/backend/ticket/repository/UserRepository.java new file mode 100644 index 0000000..1706383 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/repository/UserRepository.java @@ -0,0 +1,13 @@ +package comento.backend.ticket.repository; + +import comento.backend.ticket.domain.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface UserRepository extends JpaRepository { + + Optional findByEmail(String email); +} diff --git a/ticket/src/main/java/comento/backend/ticket/service/UserService.java b/ticket/src/main/java/comento/backend/ticket/service/UserService.java new file mode 100644 index 0000000..05472d8 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/service/UserService.java @@ -0,0 +1,42 @@ +package comento.backend.ticket.service; + +import comento.backend.ticket.domain.User; +import comento.backend.ticket.dto.UserDto; +import comento.backend.ticket.repository.UserRepository; +import org.springframework.stereotype.Service; + +import java.util.regex.Pattern; + + +@Service +public class UserService { + private final UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + //이메일 등록 + public User saveUser(UserDto userDto){ + User user = userDto.toEntity(); + if(verifyEmail(user.getEmail())) + if(checkEmailDuplicate(user.getEmail())) + throw new RuntimeException("409"); + else + return userRepository.save(user); + else + throw new RuntimeException("400"); + } + + //이메일 검증 (이메일 형식인지 확인) + private boolean verifyEmail(String email) { + String email2 = email; + String regex = "^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$"; + return Pattern.matches(regex, email2); + } + + //이메일 중복 확인 + private boolean checkEmailDuplicate(String email){ + return userRepository.findByEmail(email).isPresent() ? true : false; + } +} diff --git a/ticket/src/main/resources/logback.xml b/ticket/src/main/resources/logback.xml new file mode 100644 index 0000000..b361d71 --- /dev/null +++ b/ticket/src/main/resources/logback.xml @@ -0,0 +1,29 @@ + + + + + + + + + logs/ticket-reservation-server.log + + + logs/ticket-reservation-server-%d{yyyy-MM-dd}.log + + 30 + + + ${LOG_PATTERN} + + + + + + + + + + + + \ No newline at end of file diff --git a/ticket/src/test/java/comento/backend/ticket/repository/UserRepositryTest.java b/ticket/src/test/java/comento/backend/ticket/repository/UserRepositryTest.java new file mode 100644 index 0000000..3485dda --- /dev/null +++ b/ticket/src/test/java/comento/backend/ticket/repository/UserRepositryTest.java @@ -0,0 +1,39 @@ +package comento.backend.ticket.repository; + +import comento.backend.ticket.domain.User; +import comento.backend.ticket.service.UserService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +//given +//when +//then + +@SpringBootTest +@Transactional +public class UserRepositryTest { + private final UserRepository userRepository; + + @Autowired + public UserRepositryTest(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Test + public void 사용자_등록(){ + //given + User user = new User(); + user.setEmail("kimhyejung12@naver.com"); + + //when + User result = userRepository.save(user); + + //then + if(result != null) + System.out.println(result.getId()); + else + System.out.println("실패"); + } +} diff --git a/ticket/src/test/java/comento/backend/ticket/service/UserServiceTest.java b/ticket/src/test/java/comento/backend/ticket/service/UserServiceTest.java new file mode 100644 index 0000000..2282a9f --- /dev/null +++ b/ticket/src/test/java/comento/backend/ticket/service/UserServiceTest.java @@ -0,0 +1,50 @@ +package comento.backend.ticket.service; + +import comento.backend.ticket.domain.User; +import comento.backend.ticket.dto.UserDto; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +//given +//when +//then + +@SpringBootTest +@Transactional +public class UserServiceTest { + private final UserService userService; + + @Autowired + public UserServiceTest(UserService userService) { + this.userService = userService; + } + + @Test + public void 이메일_등록(){ + //given + UserDto userDto = new UserDto("kimhyejung12@naver.com"); + UserDto userDto2 = new UserDto("kimhyejung12@naver.com"); //error 409 + + //when + Optional result = Optional.ofNullable(userService.saveUser(userDto)); + Optional result2 = Optional.ofNullable(userService.saveUser(userDto2)); + + //then + result.ifPresent(selectUser -> { + System.out.println(selectUser.getId()); + System.out.println(selectUser.getEmail()); + System.out.println(selectUser.getCreate_at()); + }); + + result2.ifPresent(selectUser -> { + System.out.println(selectUser.getId()); + System.out.println(selectUser.getEmail()); + System.out.println(selectUser.getCreate_at()); + }); + } + +} From aab510ec4e0a27274d833f16e19616e7defd5d11 Mon Sep 17 00:00:00 2001 From: hyejungg Date: Tue, 21 Sep 2021 23:46:23 +0900 Subject: [PATCH 2/8] =?UTF-8?q?[fix:#9]=20=ED=94=BC=EB=93=9C=EB=B0=B1=20?= =?UTF-8?q?=EB=B0=94=ED=83=95=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 피드백을 바탕으로 크게 다음과 같이 수정하였습니다. 1. `@Vaildated` 의존성 라이브러리를 추가하여 유효성 검증하도록 수정하면서 관련 메소드 삭제, UserDto에 `@Email` 어노테이션 추가 2. `GlobalExceptionHandler`에서 ~Exception 이라는 사용자 정의 예외를 받아서 처리되도록 수정 3. 예외 혹은 성공 return 시 postman에서 Status 부분이 모두 200 OK로 지정되는 문제가 있어서 각각 ResponseEntity로 return하도록 수정하였음... --- ticket/build.gradle | 2 + .../backend/ticket/config/ErrorCode.java | 4 +- .../backend/ticket/config/ErrorResponse.java | 7 +- .../ticket/config/GlobalExceptionHandler.java | 85 ++++++++++--------- .../ticket/config/SuccessResponse.java | 5 +- .../customException/DuplicatedException.java | 10 +++ .../customException/NoAuthException.java | 10 +++ .../NotFoundDataException.java | 10 +++ .../ticket/controller/UserController.java | 12 ++- .../comento/backend/ticket/domain/User.java | 6 +- .../comento/backend/ticket/dto/UserDto.java | 5 ++ .../backend/ticket/service/UserService.java | 26 ++---- .../ticket/repository/UserRepositryTest.java | 8 +- .../ticket/service/UserServiceTest.java | 6 +- 14 files changed, 114 insertions(+), 82 deletions(-) create mode 100644 ticket/src/main/java/comento/backend/ticket/config/customException/DuplicatedException.java create mode 100644 ticket/src/main/java/comento/backend/ticket/config/customException/NoAuthException.java create mode 100644 ticket/src/main/java/comento/backend/ticket/config/customException/NotFoundDataException.java diff --git a/ticket/build.gradle b/ticket/build.gradle index a2d3c42..8132f27 100644 --- a/ticket/build.gradle +++ b/ticket/build.gradle @@ -22,6 +22,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' + // validation + implementation 'org.springframework.boot:spring-boot-starter-validation' compileOnly 'org.projectlombok:lombok' runtimeOnly 'mysql:mysql-connector-java' annotationProcessor 'org.projectlombok:lombok' diff --git a/ticket/src/main/java/comento/backend/ticket/config/ErrorCode.java b/ticket/src/main/java/comento/backend/ticket/config/ErrorCode.java index c69b420..8769d25 100644 --- a/ticket/src/main/java/comento/backend/ticket/config/ErrorCode.java +++ b/ticket/src/main/java/comento/backend/ticket/config/ErrorCode.java @@ -14,8 +14,8 @@ public enum ErrorCode { INVALID_VALUE(400, HttpStatus.BAD_REQUEST, "BAD REQUEST", "잘못된 요청입니다. 다시 요청해주세요."), NO_USER(401, HttpStatus.UNAUTHORIZED,"UNAUTHORIZED", "등록되지 않은 사용자입니다"), NO_DATA(404, HttpStatus.NOT_FOUND, "NOT FOUND ERROR", "요청할 수 없는 리소스입니다."), - VALID_USER(409, HttpStatus.CONFLICT, "CONFLICT", "이미 존재하는 사용자입니다."), - VALID_SEAT(409, HttpStatus.CONFLICT, "CONFLICT", "이미 예약된 좌석입니다."), + INVALID_USER(409, HttpStatus.CONFLICT, "CONFLICT", "이미 존재하는 사용자입니다."), + INVALID_SEAT(409, HttpStatus.CONFLICT, "CONFLICT", "이미 예약된 좌석입니다."), SERVER_ERROR(500, HttpStatus.INTERNAL_SERVER_ERROR, "INTERVAL_SERVER ERROR", "서버 에러입니다."); private final Integer status; diff --git a/ticket/src/main/java/comento/backend/ticket/config/ErrorResponse.java b/ticket/src/main/java/comento/backend/ticket/config/ErrorResponse.java index 374c600..93d3254 100644 --- a/ticket/src/main/java/comento/backend/ticket/config/ErrorResponse.java +++ b/ticket/src/main/java/comento/backend/ticket/config/ErrorResponse.java @@ -3,24 +3,21 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; -import org.springframework.http.HttpStatus; @Data @AllArgsConstructor @Builder -public class ErrorResponse { +public class ErrorResponse{ private Integer status; private String message; private String reason; - private HttpStatus httpStatus; //공통된 Error Message를 전송할 때 사용 - public static ErrorResponse res(final Integer status, final String message, final String reason, final HttpStatus httpStatus){ + public static ErrorResponse res(final Integer status, final String message, final String reason){ return ErrorResponse.builder() .status(status) .message(message) .reason(reason) - .httpStatus(httpStatus) .build(); } } diff --git a/ticket/src/main/java/comento/backend/ticket/config/GlobalExceptionHandler.java b/ticket/src/main/java/comento/backend/ticket/config/GlobalExceptionHandler.java index 0090c0f..c6b88fe 100644 --- a/ticket/src/main/java/comento/backend/ticket/config/GlobalExceptionHandler.java +++ b/ticket/src/main/java/comento/backend/ticket/config/GlobalExceptionHandler.java @@ -1,12 +1,21 @@ package comento.backend.ticket.config; -import comento.backend.ticket.service.UserService; +import com.fasterxml.jackson.core.JsonParseException; +import comento.backend.ticket.config.customException.DuplicatedException; +import comento.backend.ticket.config.customException.NoAuthException; +import comento.backend.ticket.config.customException.NotFoundDataException; +import javassist.NotFoundException; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import javax.validation.ConstraintViolationException; +import java.util.NoSuchElementException; + @RestControllerAdvice //@Conroller 전역에서 발생 가능한 예외를 잡아 처리 @Slf4j public class GlobalExceptionHandler { @@ -16,49 +25,49 @@ public class GlobalExceptionHandler { * 특정 Exception을 지정하여 별도로 처리 */ - @ExceptionHandler(MethodArgumentTypeMismatchException.class) - public static ErrorResponse missMatchExceptionHandler(MethodArgumentTypeMismatchException e){ + //ConstraintViolationException.class 는 유효성 검사 실패시 (@Validated) + @ExceptionHandler({JsonParseException.class, MethodArgumentNotValidException.class, ConstraintViolationException.class, MethodArgumentTypeMismatchException.class}) + public static ResponseEntity missMatchExceptionHandler(Throwable t){ errorCode = ErrorCode.INVALID_VALUE; + log.error(errorCode.getStatus() + errorCode.getMessage(), t); + return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()), + errorCode.getHttpStatus()); + } + + @ExceptionHandler(NoAuthException.class) + public static ResponseEntity noAuthExceptionHandler(NoAuthException e){ + errorCode = ErrorCode.NO_USER; log.error(errorCode.getStatus() + errorCode.getMessage(), e); - return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); + return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()), + errorCode.getHttpStatus()); } - @ExceptionHandler(IllegalStateException.class) - public static ErrorResponse illegalExceptionHandler(IllegalStateException ie){ - errorCode = ErrorCode.SERVER_ERROR; - log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), ie); - return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); + @ExceptionHandler({NoSuchElementException.class, NotFoundException.class, NotFoundDataException.class}) + public static ResponseEntity notFoundExceptionHandler(Throwable t){ + errorCode = ErrorCode.NO_DATA; + log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), t); + return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()), + errorCode.getHttpStatus()); } - @ExceptionHandler(RuntimeException.class) - public static ErrorResponse exceptionHandler(RuntimeException e){ - switch(e.getMessage()){ - case "400" : - errorCode = ErrorCode.INVALID_VALUE; - log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e); - return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); - case "401" : - errorCode = ErrorCode.NO_USER; - log.error(errorCode.getStatus() + " " + errorCode.getMessage(), e); - return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); - case "404" : - errorCode = ErrorCode.NO_DATA; - log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e); - return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); - case "409" : - if (e.getClass().getSimpleName().equals("UserService")) { //호출된 곳이 UserService - errorCode = ErrorCode.VALID_USER; - log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e); - return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); - }else{ //좌석 예약 시 - errorCode = ErrorCode.VALID_SEAT; - log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e); - return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); - } - default : - errorCode = ErrorCode.SERVER_ERROR; - log.error(errorCode.getStatus() + errorCode.getMessage(), e); - return ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason(), errorCode.getHttpStatus()); + @ExceptionHandler(DuplicatedException.class) + public static ResponseEntity duplicateExceptionHandler(DuplicatedException e){ + if (e.getMessage() == "UserService") { //호출된 곳이 UserService + errorCode = ErrorCode.INVALID_USER; + log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e); + }else{ //좌석 예약 시 + errorCode = ErrorCode.INVALID_SEAT; + log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e); } + return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()), + errorCode.getHttpStatus()); + } + + @ExceptionHandler({IllegalStateException.class, RuntimeException.class}) + public static ResponseEntity IllExceptionHandler(Throwable t){ + errorCode = ErrorCode.SERVER_ERROR; + log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), t); + return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()), + errorCode.getHttpStatus()); } } diff --git a/ticket/src/main/java/comento/backend/ticket/config/SuccessResponse.java b/ticket/src/main/java/comento/backend/ticket/config/SuccessResponse.java index 39aaf0d..e491cea 100644 --- a/ticket/src/main/java/comento/backend/ticket/config/SuccessResponse.java +++ b/ticket/src/main/java/comento/backend/ticket/config/SuccessResponse.java @@ -3,7 +3,6 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; -import org.springframework.http.HttpStatus; @Data @AllArgsConstructor @@ -12,15 +11,13 @@ public class SuccessResponse { private Integer status; private String message; private Object data; - private HttpStatus httpStatus; //공통된 Success Message를 전송할 때 사용 - public static SuccessResponse res(final Integer status, final String message, final Object data, final HttpStatus httpStatus){ + public static SuccessResponse res(final Integer status, final String message, final Object data){ return SuccessResponse.builder() .status(status) .message(message) .data(data) - .httpStatus(httpStatus) .build(); } diff --git a/ticket/src/main/java/comento/backend/ticket/config/customException/DuplicatedException.java b/ticket/src/main/java/comento/backend/ticket/config/customException/DuplicatedException.java new file mode 100644 index 0000000..bcf6588 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/config/customException/DuplicatedException.java @@ -0,0 +1,10 @@ +package comento.backend.ticket.config.customException; + +public class DuplicatedException extends RuntimeException{ + public DuplicatedException() { + } + + public DuplicatedException(String message) { + super(message); + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/config/customException/NoAuthException.java b/ticket/src/main/java/comento/backend/ticket/config/customException/NoAuthException.java new file mode 100644 index 0000000..f264136 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/config/customException/NoAuthException.java @@ -0,0 +1,10 @@ +package comento.backend.ticket.config.customException; + +public class NoAuthException extends RuntimeException{ + public NoAuthException() { + } + + public NoAuthException(String message) { + super(message); + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/config/customException/NotFoundDataException.java b/ticket/src/main/java/comento/backend/ticket/config/customException/NotFoundDataException.java new file mode 100644 index 0000000..c53f237 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/config/customException/NotFoundDataException.java @@ -0,0 +1,10 @@ +package comento.backend.ticket.config.customException; + +public class NotFoundDataException extends RuntimeException{ + public NotFoundDataException() { + } + + public NotFoundDataException(String message) { + super(message); + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/controller/UserController.java b/ticket/src/main/java/comento/backend/ticket/controller/UserController.java index b376312..21e6c2e 100644 --- a/ticket/src/main/java/comento/backend/ticket/controller/UserController.java +++ b/ticket/src/main/java/comento/backend/ticket/controller/UserController.java @@ -2,6 +2,7 @@ import comento.backend.ticket.config.SuccessCode; import comento.backend.ticket.config.SuccessResponse; +import comento.backend.ticket.config.customException.NotFoundDataException; import comento.backend.ticket.domain.User; import comento.backend.ticket.dto.UserDto; import comento.backend.ticket.service.UserService; @@ -22,19 +23,16 @@ public UserController(UserService userService) { this.userService = userService; } - //이메일 등록 @PostMapping("/signup") - public SuccessResponse addEmail(@Validated @RequestBody String email){ - UserDto userDto = new UserDto(email); - System.out.println(userDto.toString()); + public ResponseEntity addEmail(@Validated @RequestBody UserDto userDto){ User receive = userService.saveUser(userDto); if(receive != null){ successCode = SuccessCode.CREATED; - return SuccessResponse.res( - successCode.getStatus(), successCode.getMessage(), CREATED_MSG , successCode.getHttpStatus()); + return new ResponseEntity<>(SuccessResponse.res( + successCode.getStatus(), successCode.getMessage(), CREATED_MSG), successCode.getHttpStatus()); }else{ - throw new RuntimeException("404"); + throw new NotFoundDataException(); } } diff --git a/ticket/src/main/java/comento/backend/ticket/domain/User.java b/ticket/src/main/java/comento/backend/ticket/domain/User.java index ac79a47..755b7c6 100644 --- a/ticket/src/main/java/comento/backend/ticket/domain/User.java +++ b/ticket/src/main/java/comento/backend/ticket/domain/User.java @@ -21,10 +21,10 @@ public class User{ @Column(nullable = true) private String email; - @Column() + @Column(name="create_at") @CreationTimestamp //Entity가 생성되어 저장될 때 시간이 자동 저장 @Temporal(TemporalType.TIMESTAMP) - private Date create_at; + private Date createAt; public User(){} @@ -32,6 +32,6 @@ public User(){} public User(long id, String email, Date create_at) { this.id = id; this.email = email; - this.create_at = create_at; + this.createAt = createAt; } } diff --git a/ticket/src/main/java/comento/backend/ticket/dto/UserDto.java b/ticket/src/main/java/comento/backend/ticket/dto/UserDto.java index c45c80a..f837c3d 100644 --- a/ticket/src/main/java/comento/backend/ticket/dto/UserDto.java +++ b/ticket/src/main/java/comento/backend/ticket/dto/UserDto.java @@ -4,11 +4,16 @@ import lombok.Data; import lombok.Getter; +import javax.validation.constraints.Email; + @Getter @Data public class UserDto { + @Email private String email; + public UserDto(){} + public UserDto(String email) { this.email = email; } diff --git a/ticket/src/main/java/comento/backend/ticket/service/UserService.java b/ticket/src/main/java/comento/backend/ticket/service/UserService.java index 05472d8..859f2ac 100644 --- a/ticket/src/main/java/comento/backend/ticket/service/UserService.java +++ b/ticket/src/main/java/comento/backend/ticket/service/UserService.java @@ -1,11 +1,12 @@ package comento.backend.ticket.service; +import comento.backend.ticket.config.customException.DuplicatedException; import comento.backend.ticket.domain.User; import comento.backend.ticket.dto.UserDto; import comento.backend.ticket.repository.UserRepository; import org.springframework.stereotype.Service; -import java.util.regex.Pattern; +import java.util.Objects; @Service @@ -16,27 +17,18 @@ public UserService(UserRepository userRepository) { this.userRepository = userRepository; } - //이메일 등록 public User saveUser(UserDto userDto){ User user = userDto.toEntity(); - if(verifyEmail(user.getEmail())) - if(checkEmailDuplicate(user.getEmail())) - throw new RuntimeException("409"); - else - return userRepository.save(user); - else - throw new RuntimeException("400"); - } - //이메일 검증 (이메일 형식인지 확인) - private boolean verifyEmail(String email) { - String email2 = email; - String regex = "^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$"; - return Pattern.matches(regex, email2); + if(checkEmailDuplicate(user.getEmail())) { + return userRepository.save(user); + }else{ + throw new DuplicatedException("UserService"); + } } - //이메일 중복 확인 private boolean checkEmailDuplicate(String email){ - return userRepository.findByEmail(email).isPresent() ? true : false; + User user = userRepository.findByEmail(email).orElseGet(()->null); + return Objects.isNull(user); //null이면 true } } diff --git a/ticket/src/test/java/comento/backend/ticket/repository/UserRepositryTest.java b/ticket/src/test/java/comento/backend/ticket/repository/UserRepositryTest.java index 3485dda..4202449 100644 --- a/ticket/src/test/java/comento/backend/ticket/repository/UserRepositryTest.java +++ b/ticket/src/test/java/comento/backend/ticket/repository/UserRepositryTest.java @@ -2,10 +2,12 @@ import comento.backend.ticket.domain.User; import comento.backend.ticket.service.UserService; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; +import static org.assertj.core.api.Assertions.assertThat; //given //when @@ -22,6 +24,7 @@ public UserRepositryTest(UserRepository userRepository) { } @Test + @DisplayName("이메일을 입력하면 사용자 등록에 성공하여 DB에 저장한다.") public void 사용자_등록(){ //given User user = new User(); @@ -31,9 +34,6 @@ public UserRepositryTest(UserRepository userRepository) { User result = userRepository.save(user); //then - if(result != null) - System.out.println(result.getId()); - else - System.out.println("실패"); + assertThat(result.getId()).isNotNull(); } } diff --git a/ticket/src/test/java/comento/backend/ticket/service/UserServiceTest.java b/ticket/src/test/java/comento/backend/ticket/service/UserServiceTest.java index 2282a9f..5ef2d29 100644 --- a/ticket/src/test/java/comento/backend/ticket/service/UserServiceTest.java +++ b/ticket/src/test/java/comento/backend/ticket/service/UserServiceTest.java @@ -2,6 +2,7 @@ import comento.backend.ticket.domain.User; import comento.backend.ticket.dto.UserDto; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -24,6 +25,7 @@ public UserServiceTest(UserService userService) { } @Test + @DisplayName("이메일을 입력하면 사용자 등록에 성공하여 DB에 저장한다.") public void 이메일_등록(){ //given UserDto userDto = new UserDto("kimhyejung12@naver.com"); @@ -37,13 +39,13 @@ public UserServiceTest(UserService userService) { result.ifPresent(selectUser -> { System.out.println(selectUser.getId()); System.out.println(selectUser.getEmail()); - System.out.println(selectUser.getCreate_at()); + System.out.println(selectUser.getCreateAt()); }); result2.ifPresent(selectUser -> { System.out.println(selectUser.getId()); System.out.println(selectUser.getEmail()); - System.out.println(selectUser.getCreate_at()); + System.out.println(selectUser.getCreateAt()); }); } From fc688fbcd9062535992db074acb4f62fcea09c5d Mon Sep 17 00:00:00 2001 From: hyejungg Date: Wed, 22 Sep 2021 00:51:25 +0900 Subject: [PATCH 3/8] =?UTF-8?q?[fix:#9]=20=ED=94=BC=EB=93=9C=EB=B0=B1=20?= =?UTF-8?q?=EB=B0=94=ED=83=95=EC=9C=BC=EB=A1=9C=20=EC=9E=AC=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 빠른 피드백 감사합니다 !!!!! 1. `UserService` 불필요한 if-else문 삭제 2. `UserController` 불필요한 null 확인 처리 삭제 3. /test 테스트 코드를 기능 별로 메소드를 생성하여 테스트하도록 수정(UserServiceTest의 타입오류로 인한 테스트는 추후에 수정 필요해 보임..) --- .../ticket/controller/UserController.java | 16 ++------ .../backend/ticket/service/UserService.java | 6 +-- .../ticket/repository/UserRepositryTest.java | 16 +++++++- .../ticket/service/UserServiceTest.java | 37 +++++++++++++++++-- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/ticket/src/main/java/comento/backend/ticket/controller/UserController.java b/ticket/src/main/java/comento/backend/ticket/controller/UserController.java index 21e6c2e..9848d07 100644 --- a/ticket/src/main/java/comento/backend/ticket/controller/UserController.java +++ b/ticket/src/main/java/comento/backend/ticket/controller/UserController.java @@ -14,7 +14,7 @@ @RestController @RequestMapping("/api") public class UserController { - private SuccessCode successCode; + private SuccessCode successCode = SuccessCode.CREATED; private final UserService userService; private static final String CREATED_MSG = "등록 성공"; @@ -25,16 +25,8 @@ public UserController(UserService userService) { @PostMapping("/signup") public ResponseEntity addEmail(@Validated @RequestBody UserDto userDto){ - User receive = userService.saveUser(userDto); - - if(receive != null){ - successCode = SuccessCode.CREATED; - return new ResponseEntity<>(SuccessResponse.res( - successCode.getStatus(), successCode.getMessage(), CREATED_MSG), successCode.getHttpStatus()); - }else{ - throw new NotFoundDataException(); - } + userService.saveUser(userDto); + return new ResponseEntity<>(SuccessResponse.res(successCode.getStatus(), successCode.getMessage(), CREATED_MSG), + successCode.getHttpStatus()); } - - } diff --git a/ticket/src/main/java/comento/backend/ticket/service/UserService.java b/ticket/src/main/java/comento/backend/ticket/service/UserService.java index 859f2ac..631ea57 100644 --- a/ticket/src/main/java/comento/backend/ticket/service/UserService.java +++ b/ticket/src/main/java/comento/backend/ticket/service/UserService.java @@ -20,11 +20,11 @@ public UserService(UserRepository userRepository) { public User saveUser(UserDto userDto){ User user = userDto.toEntity(); - if(checkEmailDuplicate(user.getEmail())) { - return userRepository.save(user); - }else{ + if(!checkEmailDuplicate(user.getEmail())) { throw new DuplicatedException("UserService"); } + + return userRepository.save(user); } private boolean checkEmailDuplicate(String email){ diff --git a/ticket/src/test/java/comento/backend/ticket/repository/UserRepositryTest.java b/ticket/src/test/java/comento/backend/ticket/repository/UserRepositryTest.java index 4202449..9086af1 100644 --- a/ticket/src/test/java/comento/backend/ticket/repository/UserRepositryTest.java +++ b/ticket/src/test/java/comento/backend/ticket/repository/UserRepositryTest.java @@ -25,7 +25,7 @@ public UserRepositryTest(UserRepository userRepository) { @Test @DisplayName("이메일을 입력하면 사용자 등록에 성공하여 DB에 저장한다.") - public void 사용자_등록(){ + public void 사용자_등록_성공(){ //given User user = new User(); user.setEmail("kimhyejung12@naver.com"); @@ -36,4 +36,18 @@ public UserRepositryTest(UserRepository userRepository) { //then assertThat(result.getId()).isNotNull(); } + + @Test + @DisplayName("이메일을 입력하면 사용자 등록에 성공하여 DB에 저장한다.") + public void 사용자_등록_중복으로_인한_실패(){ + //given + User user = new User(); + user.setEmail("kimhyejung12@naver.com"); //error 409 + + //when + User result = userRepository.save(user); + + //then + assertThat(result.getId()).isNotNull(); + } } diff --git a/ticket/src/test/java/comento/backend/ticket/service/UserServiceTest.java b/ticket/src/test/java/comento/backend/ticket/service/UserServiceTest.java index 5ef2d29..ece10ad 100644 --- a/ticket/src/test/java/comento/backend/ticket/service/UserServiceTest.java +++ b/ticket/src/test/java/comento/backend/ticket/service/UserServiceTest.java @@ -26,14 +26,12 @@ public UserServiceTest(UserService userService) { @Test @DisplayName("이메일을 입력하면 사용자 등록에 성공하여 DB에 저장한다.") - public void 이메일_등록(){ + public void 이메일_등록_성공(){ //given UserDto userDto = new UserDto("kimhyejung12@naver.com"); - UserDto userDto2 = new UserDto("kimhyejung12@naver.com"); //error 409 //when Optional result = Optional.ofNullable(userService.saveUser(userDto)); - Optional result2 = Optional.ofNullable(userService.saveUser(userDto2)); //then result.ifPresent(selectUser -> { @@ -41,8 +39,39 @@ public UserServiceTest(UserService userService) { System.out.println(selectUser.getEmail()); System.out.println(selectUser.getCreateAt()); }); + } + + @Test + @DisplayName("이미 존재하는 이메일을 입력하면 사용자 등록에 실패하여 예외를 반환한다.") + public void 이메일_등록_중복으로_인한_실패(){ + //given + UserDto userDto = new UserDto("kimhyejung12@naver.com"); + UserDto userDto2 = new UserDto("kimhyejung12@naver.com"); + + //when + Optional result = Optional.ofNullable(userService.saveUser(userDto)); + Optional result2 = Optional.ofNullable(userService.saveUser(userDto2)); //error 409 - result2.ifPresent(selectUser -> { + //then + result.ifPresent(selectUser -> { + System.out.println(selectUser.getId()); + System.out.println(selectUser.getEmail()); + System.out.println(selectUser.getCreateAt()); + }); + } + + @Test + @DisplayName("이메일 형태가 아닌 경우 or 타입 오류 발생 시 사용자 등록에 실패하여 예외가 반환한다.") + public void 이메일_등록_타입오류로_인한_실패(){ //@TODO : 에러가 발생해야 정상인데 성공 ... (Postman에서는 정상적으로 에러 발생) + //given + String email = "12"; //success? + UserDto userDto = new UserDto(email); + + //when + Optional result = Optional.ofNullable(userService.saveUser(userDto)); //error 400 + + //then + result.ifPresent(selectUser -> { System.out.println(selectUser.getId()); System.out.println(selectUser.getEmail()); System.out.println(selectUser.getCreateAt()); From 3da966ef8945836d80d67b6784b0fad17d81cf55 Mon Sep 17 00:00:00 2001 From: hyejungg Date: Sat, 25 Sep 2021 15:40:11 +0900 Subject: [PATCH 4/8] =?UTF-8?q?[feat:#10]=20=EA=B3=B5=EC=97=B0=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EC=BD=94=EB=93=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 날짜로 공연 정보를 조회하는 코드 2. 날짜&이름으로 공연 정보를 조회하는 코드 --- .../ticket/config/GlobalExceptionHandler.java | 17 ++-- .../controller/PerformanceController.java | 52 ++++++++++++ .../backend/ticket/domain/Performance.java | 55 +++++++++++++ .../backend/ticket/dto/PerformanceDto.java | 35 ++++++++ .../repository/PerformanceRepository.java | 15 ++++ .../ticket/service/PerformanceService.java | 31 +++++++ .../repository/PerformanceRepositroyTest.java | 72 +++++++++++++++++ .../service/PerformanceServiceTest.java | 81 +++++++++++++++++++ 8 files changed, 352 insertions(+), 6 deletions(-) create mode 100644 ticket/src/main/java/comento/backend/ticket/controller/PerformanceController.java create mode 100644 ticket/src/main/java/comento/backend/ticket/domain/Performance.java create mode 100644 ticket/src/main/java/comento/backend/ticket/dto/PerformanceDto.java create mode 100644 ticket/src/main/java/comento/backend/ticket/repository/PerformanceRepository.java create mode 100644 ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java create mode 100644 ticket/src/test/java/comento/backend/ticket/repository/PerformanceRepositroyTest.java create mode 100644 ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest.java diff --git a/ticket/src/main/java/comento/backend/ticket/config/GlobalExceptionHandler.java b/ticket/src/main/java/comento/backend/ticket/config/GlobalExceptionHandler.java index c6b88fe..b02f14d 100644 --- a/ticket/src/main/java/comento/backend/ticket/config/GlobalExceptionHandler.java +++ b/ticket/src/main/java/comento/backend/ticket/config/GlobalExceptionHandler.java @@ -9,6 +9,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; 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 org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; @@ -26,10 +27,14 @@ public class GlobalExceptionHandler { */ //ConstraintViolationException.class 는 유효성 검사 실패시 (@Validated) - @ExceptionHandler({JsonParseException.class, MethodArgumentNotValidException.class, ConstraintViolationException.class, MethodArgumentTypeMismatchException.class}) + @ExceptionHandler({JsonParseException.class, + MethodArgumentNotValidException.class, + ConstraintViolationException.class, + MethodArgumentTypeMismatchException.class, + MissingServletRequestParameterException.class}) public static ResponseEntity missMatchExceptionHandler(Throwable t){ errorCode = ErrorCode.INVALID_VALUE; - log.error(errorCode.getStatus() + errorCode.getMessage(), t); + log.error(errorCode.getStatus() + " " + errorCode.getMessage(), t); return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()), errorCode.getHttpStatus()); } @@ -45,7 +50,7 @@ public static ResponseEntity noAuthExceptionHandler(NoAuthException e){ @ExceptionHandler({NoSuchElementException.class, NotFoundException.class, NotFoundDataException.class}) public static ResponseEntity notFoundExceptionHandler(Throwable t){ errorCode = ErrorCode.NO_DATA; - log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), t); + log.error(errorCode.getStatus() + " " + errorCode.getMessage(), t); return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()), errorCode.getHttpStatus()); } @@ -54,10 +59,10 @@ public static ResponseEntity notFoundExceptionHandler(Throwable t){ public static ResponseEntity duplicateExceptionHandler(DuplicatedException e){ if (e.getMessage() == "UserService") { //호출된 곳이 UserService errorCode = ErrorCode.INVALID_USER; - log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e); + log.error(errorCode.getStatus() + " " + errorCode.getMessage(), e); }else{ //좌석 예약 시 errorCode = ErrorCode.INVALID_SEAT; - log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e); + log.error(errorCode.getStatus() + " " + errorCode.getMessage(), e); } return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()), errorCode.getHttpStatus()); @@ -66,7 +71,7 @@ public static ResponseEntity duplicateExceptionHandler(DuplicatedException e){ @ExceptionHandler({IllegalStateException.class, RuntimeException.class}) public static ResponseEntity IllExceptionHandler(Throwable t){ errorCode = ErrorCode.SERVER_ERROR; - log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), t); + log.error(errorCode.getStatus() + " " + errorCode.getMessage(), t); return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()), errorCode.getHttpStatus()); } diff --git a/ticket/src/main/java/comento/backend/ticket/controller/PerformanceController.java b/ticket/src/main/java/comento/backend/ticket/controller/PerformanceController.java new file mode 100644 index 0000000..da0f0ea --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/controller/PerformanceController.java @@ -0,0 +1,52 @@ +package comento.backend.ticket.controller; + +import comento.backend.ticket.config.SuccessCode; +import comento.backend.ticket.config.SuccessResponse; +import comento.backend.ticket.config.customException.NotFoundDataException; +import comento.backend.ticket.domain.Performance; +import comento.backend.ticket.dto.PerformanceDto; +import comento.backend.ticket.service.PerformanceService; +import comento.backend.ticket.service.SeatService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.annotation.DateTimeFormat; +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.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.*; +import java.util.Date; + +@RestController +@RequestMapping("/api/performance") +public class PerformanceController { + private final PerformanceService performanceService; + private final SeatService seatService; + private SuccessCode successCode = SuccessCode.OK; + + @Autowired + public PerformanceController(PerformanceService performanceService, SeatService seatService) { + this.performanceService = performanceService; + this.seatService = seatService; + } + + @GetMapping("/info") + public ResponseEntity showPerformanceInfo(@Valid @RequestParam(value = "date", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") Date date, + @Valid @RequestParam(value = "title", required = false) String title) { + PerformanceDto performanceDto = new PerformanceDto(title, date); + List result; + result = performanceDto.getTitle() != null ? + performanceService.getListByDateAndTitle(performanceDto) : performanceService.getListByDate(performanceDto); + + if(result.isEmpty()){ + throw new NotFoundDataException(); + } + + return new ResponseEntity(SuccessResponse.res(successCode.getStatus(), successCode.getMessage(), result), + HttpStatus.OK); + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/domain/Performance.java b/ticket/src/main/java/comento/backend/ticket/domain/Performance.java new file mode 100644 index 0000000..b6dfffa --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/domain/Performance.java @@ -0,0 +1,55 @@ +package comento.backend.ticket.domain; + +import lombok.Builder; +import lombok.Data; + +import javax.persistence.*; +import java.util.Date; + +@Entity +@Data +@Table(name="Performance") +public class Performance { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="performance_id") + private Long id; + + @Column + private String title; + + @Column + private String genere; + + @Column(name="start_date") + @Temporal(TemporalType.DATE) + private Date startDate; + + @Column(name="end_date") + @Temporal(TemporalType.DATE) + private Date endDate; + + @Column(columnDefinition = "TEXT") + private String price; + + @Column(columnDefinition = "TEXT") + private String description; + + @Column(name = "running_time") + private String runningTime; + + public Performance(){} + + @Builder + public Performance(Long id, String title, String genere, Date startDate, Date endDate, String price, String description, String runningTime) { + this.id = id; + this.title = title; + this.genere = genere; + this.startDate = startDate; + this.endDate = endDate; + this.price = price; + this.description = description; + this.runningTime = runningTime; + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/dto/PerformanceDto.java b/ticket/src/main/java/comento/backend/ticket/dto/PerformanceDto.java new file mode 100644 index 0000000..7912006 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/dto/PerformanceDto.java @@ -0,0 +1,35 @@ +package comento.backend.ticket.dto; + +import lombok.Getter; +import lombok.Setter; + +import java.util.Date; + +@Getter +@Setter +public class PerformanceDto { + private String title; + private Date startDate; + private Date endDate; + private String price; + private String genere; + private String description; + private String runningTime; + + public PerformanceDto(){} + + public PerformanceDto(String title, Date startDate) { + this.title = title; + this.startDate = startDate; + } + + public PerformanceDto(String title, Date startDate, Date endDate, String price, String genere, String description, String runningTime) { + this.title = title; + this.startDate = startDate; + this.endDate = endDate; + this.price = price; + this.genere = genere; + this.description = description; + this.runningTime = runningTime; + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/repository/PerformanceRepository.java b/ticket/src/main/java/comento/backend/ticket/repository/PerformanceRepository.java new file mode 100644 index 0000000..0957bf6 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/repository/PerformanceRepository.java @@ -0,0 +1,15 @@ +package comento.backend.ticket.repository; + +import comento.backend.ticket.domain.Performance; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Date; +import java.util.List; + +@Repository +public interface PerformanceRepository extends JpaRepository { + //@TODO : 쿼리 다시 짜기! + List findByStartDateGreaterThanEqualOrderByStartDateAsc(Date startDate); + List findByTitleAndStartDateGreaterThanEqualOrderByStartDate(String title, Date startDate); +} diff --git a/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java b/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java new file mode 100644 index 0000000..14d552f --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java @@ -0,0 +1,31 @@ +package comento.backend.ticket.service; + +import comento.backend.ticket.domain.Performance; +import comento.backend.ticket.dto.PerformanceDto; +import comento.backend.ticket.repository.PerformanceRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Date; + +@Service +public class PerformanceService { + private final PerformanceRepository performanceRepository; + + @Autowired + public PerformanceService(PerformanceRepository performanceRepository) { + this.performanceRepository = performanceRepository; + } + + public List getListByDate(PerformanceDto performanceDto){ + Date startDate = performanceDto.getStartDate(); + return performanceRepository.findByStartDateGreaterThanEqualOrderByStartDateAsc(startDate); + } + + public List getListByDateAndTitle(PerformanceDto performanceDto){ + Date startDate = performanceDto.getStartDate(); + String title = performanceDto.getTitle(); + return performanceRepository.findByTitleAndStartDateGreaterThanEqualOrderByStartDate(title, startDate); + } +} diff --git a/ticket/src/test/java/comento/backend/ticket/repository/PerformanceRepositroyTest.java b/ticket/src/test/java/comento/backend/ticket/repository/PerformanceRepositroyTest.java new file mode 100644 index 0000000..f9f6d67 --- /dev/null +++ b/ticket/src/test/java/comento/backend/ticket/repository/PerformanceRepositroyTest.java @@ -0,0 +1,72 @@ +package comento.backend.ticket.repository; + +import comento.backend.ticket.domain.Performance; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@Transactional +public class PerformanceRepositroyTest { + private final PerformanceRepository performanceRepository; + + @Autowired + public PerformanceRepositroyTest(PerformanceRepository performanceRepository) { + this.performanceRepository = performanceRepository; + } + + @Test + @DisplayName("[성공] 날짜를 정확하게 입력한 경우") + public void 공연_날짜_정보_조회() throws ParseException { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = format.parse("2020-06-20"); + + List result = performanceRepository.findByStartDateGreaterThanEqualOrderByStartDateAsc(startDate); + + assertThat(result.size()).isNotNull(); + } + + @Test + @DisplayName("[성공] 날짜, 공연제목을 정확하게 입력한 경우") + public void 공연_날짜이름_정보_조회() throws ParseException { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = format.parse("2020-06-20"); + String title = "국립무용단 <산조>"; + + List result = performanceRepository.findByTitleAndStartDateGreaterThanEqualOrderByStartDate(title, startDate); + + assertThat(result.size()).isNotNull(); + } + + @Test + @DisplayName("[실패] NOT FOUND ERROR") + public void 공연_날짜_정보_조회실패() throws ParseException { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = format.parse("2021-06-31"); + + List result = performanceRepository.findByStartDateGreaterThanEqualOrderByStartDateAsc(startDate); + + assertThat(result).isNull(); + } + + @Test + @DisplayName("[실패] BAD REQUEST") + public void 공연_날짜이름_정보_조회실패() throws ParseException { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = format.parse("ㄴ"); + String title = "국립무용단 <산조>"; + + List result = performanceRepository.findByTitleAndStartDateGreaterThanEqualOrderByStartDate(title, startDate); + + assertThat(result).isNull(); + } +} diff --git a/ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest.java b/ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest.java new file mode 100644 index 0000000..2ea375f --- /dev/null +++ b/ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest.java @@ -0,0 +1,81 @@ +package comento.backend.ticket.service; + +import comento.backend.ticket.domain.Performance; +import comento.backend.ticket.dto.PerformanceDto; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@Transactional +public class PerformanceServiceTest { + private final PerformanceService performanceService; + + @Autowired + public PerformanceServiceTest(PerformanceService performanceService) { + this.performanceService = performanceService; + } + + @Test + @DisplayName("[성공] 날짜를 정확하게 입력한 경우") + public void 공연_날짜_정보_조회() throws ParseException { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = format.parse("2020-06-20"); + + PerformanceDto dto = new PerformanceDto(null, startDate); + + List result = performanceService.getListByDate(dto); + + assertThat(result.size()).isNotNull(); + } + + @Test + @DisplayName("[성공] 날짜, 공연제목을 정확하게 입력한 경우") + public void 공연_날짜이름_정보_조회() throws ParseException { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = format.parse("2020-06-20"); + String title = "국립무용단 <산조>"; + + PerformanceDto dto = new PerformanceDto(title, startDate); + + List result = performanceService.getListByDateAndTitle(dto); + + assertThat(result.size()).isNotNull(); + } + + @Test + @DisplayName("[실패] NOT FOUND ERROR") + public void 공연_날짜_정보_조회실패() throws ParseException { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = format.parse("2021-06-31"); + String title = "국립무용단 <산조>"; + + PerformanceDto dto = new PerformanceDto(title, startDate); + + List result = performanceService.getListByDateAndTitle(dto); + assertThat(result).isNull(); + } + + @Test + @DisplayName("[실패] BAD REQUEST") + public void 공연_날짜이름_정보_조회실패() throws ParseException { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = format.parse("S"); + String title = "국립무용단 <산조>"; + + PerformanceDto dto = new PerformanceDto(title, startDate); + + List result = performanceService.getListByDateAndTitle(dto); + assertThat(result).isNull(); + } +} From 7ab4c974ab6a11cae4eb80d093eddbd104002153 Mon Sep 17 00:00:00 2001 From: hyejungg Date: Mon, 27 Sep 2021 16:42:17 +0900 Subject: [PATCH 5/8] =?UTF-8?q?[fix:#10]=20performanceService=20=EC=9C=A0?= =?UTF-8?q?=EB=8B=9B=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EB=B9=84=EC=A6=88=EB=8B=88=EC=8A=A4=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 피드백을 바탕으로 다음과 같이 수정하였습니다. 1. Controller에서 검사하던 내용을 -> Service 계층으로 옮김 2. PerformanceResponse를 추가하면서 관련 코드 수정 3. PerformanceService 계층 유닛테스트 추가(수정 필요) --- [질문] `PerformanceServiceTest2.java` 에서 //@TODO 주석으로 적어두었지만... 테스트 결과가 성공해야하는데 실패하는 이상한 문제가 있습니다 ........ 뭐가 문제인지 정확히 모르겠지만 given().willReturn()과 관련이 있는걸까요?! 성공해야하는데 NotFoundDataException이 발생한다고 합니다. (Service 에서 getListPerformance에서 if문에 해당되어..) --- .../controller/PerformanceController.java | 12 +-- .../ticket/dto/PerformanceResponse.java | 43 +++++++++ .../repository/PerformanceRepository.java | 1 - .../ticket/service/PerformanceService.java | 20 ++-- .../repository/PerformanceRepositroyTest.java | 29 ++++-- .../service/PerformanceServiceTest.java | 27 +++--- .../service/PerformanceServiceTest2.java | 92 +++++++++++++++++++ 7 files changed, 187 insertions(+), 37 deletions(-) create mode 100644 ticket/src/main/java/comento/backend/ticket/dto/PerformanceResponse.java create mode 100644 ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest2.java diff --git a/ticket/src/main/java/comento/backend/ticket/controller/PerformanceController.java b/ticket/src/main/java/comento/backend/ticket/controller/PerformanceController.java index da0f0ea..437b745 100644 --- a/ticket/src/main/java/comento/backend/ticket/controller/PerformanceController.java +++ b/ticket/src/main/java/comento/backend/ticket/controller/PerformanceController.java @@ -2,9 +2,9 @@ import comento.backend.ticket.config.SuccessCode; import comento.backend.ticket.config.SuccessResponse; -import comento.backend.ticket.config.customException.NotFoundDataException; import comento.backend.ticket.domain.Performance; import comento.backend.ticket.dto.PerformanceDto; +import comento.backend.ticket.dto.PerformanceResponse; import comento.backend.ticket.service.PerformanceService; import comento.backend.ticket.service.SeatService; import org.springframework.beans.factory.annotation.Autowired; @@ -19,6 +19,8 @@ import javax.validation.Valid; import java.util.*; import java.util.Date; +import java.util.stream.Collectors; +import java.util.stream.Stream; @RestController @RequestMapping("/api/performance") @@ -38,13 +40,7 @@ public ResponseEntity showPerformanceInfo(@Valid @RequestParam(value = "date", r @DateTimeFormat(pattern = "yyyy-MM-dd") Date date, @Valid @RequestParam(value = "title", required = false) String title) { PerformanceDto performanceDto = new PerformanceDto(title, date); - List result; - result = performanceDto.getTitle() != null ? - performanceService.getListByDateAndTitle(performanceDto) : performanceService.getListByDate(performanceDto); - - if(result.isEmpty()){ - throw new NotFoundDataException(); - } + List result = performanceService.getListPerformance(performanceDto); return new ResponseEntity(SuccessResponse.res(successCode.getStatus(), successCode.getMessage(), result), HttpStatus.OK); diff --git a/ticket/src/main/java/comento/backend/ticket/dto/PerformanceResponse.java b/ticket/src/main/java/comento/backend/ticket/dto/PerformanceResponse.java new file mode 100644 index 0000000..c823f18 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/dto/PerformanceResponse.java @@ -0,0 +1,43 @@ +package comento.backend.ticket.dto; + +import comento.backend.ticket.domain.Performance; +import lombok.Data; + +import java.util.*; + +@Data +public class PerformanceResponse { + private Long id; + private String title; + private Date startDate; + private Date endDate; + private String price; + private String genere; + private String description; + private String runningTime; + + public PerformanceResponse(Long id, String title, Date startDate, Date endDate, String price, String genere, String description, String runningTime) { + this.id = id; + this.title = title; + this.startDate = startDate; + this.endDate = endDate; + this.price = price; + this.genere = genere; + this.description = description; + this.runningTime = runningTime; + } + + //convert + public static PerformanceResponse of(Performance performance){ + return new PerformanceResponse( + performance.getId(), + performance.getTitle(), + performance.getStartDate(), + performance.getEndDate(), + performance.getPrice(), + performance.getGenere(), + performance.getDescription(), + performance.getRunningTime() + ); + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/repository/PerformanceRepository.java b/ticket/src/main/java/comento/backend/ticket/repository/PerformanceRepository.java index 0957bf6..45b2869 100644 --- a/ticket/src/main/java/comento/backend/ticket/repository/PerformanceRepository.java +++ b/ticket/src/main/java/comento/backend/ticket/repository/PerformanceRepository.java @@ -9,7 +9,6 @@ @Repository public interface PerformanceRepository extends JpaRepository { - //@TODO : 쿼리 다시 짜기! List findByStartDateGreaterThanEqualOrderByStartDateAsc(Date startDate); List findByTitleAndStartDateGreaterThanEqualOrderByStartDate(String title, Date startDate); } diff --git a/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java b/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java index 14d552f..9afa603 100644 --- a/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java +++ b/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java @@ -1,31 +1,33 @@ package comento.backend.ticket.service; +import comento.backend.ticket.config.customException.NotFoundDataException; import comento.backend.ticket.domain.Performance; import comento.backend.ticket.dto.PerformanceDto; +import comento.backend.ticket.dto.PerformanceResponse; import comento.backend.ticket.repository.PerformanceRepository; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Date; +import java.util.stream.Collectors; @Service public class PerformanceService { private final PerformanceRepository performanceRepository; - @Autowired public PerformanceService(PerformanceRepository performanceRepository) { this.performanceRepository = performanceRepository; } - public List getListByDate(PerformanceDto performanceDto){ - Date startDate = performanceDto.getStartDate(); - return performanceRepository.findByStartDateGreaterThanEqualOrderByStartDateAsc(startDate); - } - - public List getListByDateAndTitle(PerformanceDto performanceDto){ + public List getListPerformance(PerformanceDto performanceDto){ Date startDate = performanceDto.getStartDate(); String title = performanceDto.getTitle(); - return performanceRepository.findByTitleAndStartDateGreaterThanEqualOrderByStartDate(title, startDate); + List result = title != null ? + performanceRepository.findByTitleAndStartDateGreaterThanEqualOrderByStartDate(title, startDate) : + performanceRepository.findByStartDateGreaterThanEqualOrderByStartDateAsc(startDate); + if(result.isEmpty()){ + throw new NotFoundDataException(); + } + return result.stream().map(PerformanceResponse::of).collect(Collectors.toList()); } } diff --git a/ticket/src/test/java/comento/backend/ticket/repository/PerformanceRepositroyTest.java b/ticket/src/test/java/comento/backend/ticket/repository/PerformanceRepositroyTest.java index f9f6d67..48184ac 100644 --- a/ticket/src/test/java/comento/backend/ticket/repository/PerformanceRepositroyTest.java +++ b/ticket/src/test/java/comento/backend/ticket/repository/PerformanceRepositroyTest.java @@ -1,5 +1,6 @@ package comento.backend.ticket.repository; +import comento.backend.ticket.config.customException.NotFoundDataException; import comento.backend.ticket.domain.Performance; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -24,43 +25,59 @@ public PerformanceRepositroyTest(PerformanceRepository performanceRepository) { this.performanceRepository = performanceRepository; } + //Junit5 부터 접근제어자 생략 가능 (JUnit4 까지는 public이어야 했음!) + @Test @DisplayName("[성공] 날짜를 정확하게 입력한 경우") - public void 공연_날짜_정보_조회() throws ParseException { + void 공연_날짜_정보_조회() throws ParseException { DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date startDate = format.parse("2020-06-20"); List result = performanceRepository.findByStartDateGreaterThanEqualOrderByStartDateAsc(startDate); - assertThat(result.size()).isNotNull(); + assertThat(result.size()).isNotZero(); } @Test @DisplayName("[성공] 날짜, 공연제목을 정확하게 입력한 경우") - public void 공연_날짜이름_정보_조회() throws ParseException { + void 공연_날짜이름_정보_조회() throws ParseException { DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date startDate = format.parse("2020-06-20"); String title = "국립무용단 <산조>"; List result = performanceRepository.findByTitleAndStartDateGreaterThanEqualOrderByStartDate(title, startDate); - assertThat(result.size()).isNotNull(); + assertThat(result.size()).isNotZero(); } @Test @DisplayName("[실패] NOT FOUND ERROR") - public void 공연_날짜_정보_조회실패() throws ParseException { + void 공연_날짜_정보_조회실패() throws ParseException, NotFoundDataException { DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date startDate = format.parse("2021-06-31"); List result = performanceRepository.findByStartDateGreaterThanEqualOrderByStartDateAsc(startDate); assertThat(result).isNull(); + + /*앙댐 + //given + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = format.parse("2021-06-31"); + + //when + final NotFoundDataException exception = assertThrows(NotFoundDataException.class, () -> { + performanceRepository.findByStartDateGreaterThanEqualOrderByStartDateAsc(startDate); + }); + + //then + assertThat(exception.getMessage()).isNotNull(); + */ } @Test @DisplayName("[실패] BAD REQUEST") - public void 공연_날짜이름_정보_조회실패() throws ParseException { + void 공연_날짜이름_정보_조회실패() throws ParseException { DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date startDate = format.parse("ㄴ"); String title = "국립무용단 <산조>"; diff --git a/ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest.java b/ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest.java index 2ea375f..178fc14 100644 --- a/ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest.java +++ b/ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest.java @@ -2,6 +2,7 @@ import comento.backend.ticket.domain.Performance; import comento.backend.ticket.dto.PerformanceDto; +import comento.backend.ticket.dto.PerformanceResponse; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -16,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; -@SpringBootTest +@SpringBootTest //통합테스트 @Transactional public class PerformanceServiceTest { private final PerformanceService performanceService; @@ -28,54 +29,54 @@ public PerformanceServiceTest(PerformanceService performanceService) { @Test @DisplayName("[성공] 날짜를 정확하게 입력한 경우") - public void 공연_날짜_정보_조회() throws ParseException { + void 공연_날짜_정보_조회() throws ParseException { DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); - Date startDate = format.parse("2020-06-20"); + Date startDate = format.parse("2021-06-20"); PerformanceDto dto = new PerformanceDto(null, startDate); - List result = performanceService.getListByDate(dto); + List result = performanceService.getListPerformance(dto); - assertThat(result.size()).isNotNull(); + assertThat(result.size()).isNotZero(); } @Test @DisplayName("[성공] 날짜, 공연제목을 정확하게 입력한 경우") - public void 공연_날짜이름_정보_조회() throws ParseException { + void 공연_날짜이름_정보_조회() throws ParseException { DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); - Date startDate = format.parse("2020-06-20"); + Date startDate = format.parse("2021-06-20"); String title = "국립무용단 <산조>"; PerformanceDto dto = new PerformanceDto(title, startDate); - List result = performanceService.getListByDateAndTitle(dto); + List result = performanceService.getListPerformance(dto); - assertThat(result.size()).isNotNull(); + assertThat(result.size()).isNotZero(); } @Test @DisplayName("[실패] NOT FOUND ERROR") - public void 공연_날짜_정보_조회실패() throws ParseException { + void 공연_날짜_정보_조회실패() throws ParseException { DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date startDate = format.parse("2021-06-31"); String title = "국립무용단 <산조>"; PerformanceDto dto = new PerformanceDto(title, startDate); - List result = performanceService.getListByDateAndTitle(dto); + List result = performanceService.getListPerformance(dto); assertThat(result).isNull(); } @Test @DisplayName("[실패] BAD REQUEST") - public void 공연_날짜이름_정보_조회실패() throws ParseException { + void 공연_날짜이름_정보_조회실패() throws ParseException { DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date startDate = format.parse("S"); String title = "국립무용단 <산조>"; PerformanceDto dto = new PerformanceDto(title, startDate); - List result = performanceService.getListByDateAndTitle(dto); + List result = performanceService.getListPerformance(dto); assertThat(result).isNull(); } } diff --git a/ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest2.java b/ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest2.java new file mode 100644 index 0000000..6030887 --- /dev/null +++ b/ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest2.java @@ -0,0 +1,92 @@ +package comento.backend.ticket.service; + +import comento.backend.ticket.config.customException.NotFoundDataException; +import comento.backend.ticket.domain.Performance; +import comento.backend.ticket.dto.PerformanceDto; +import comento.backend.ticket.dto.PerformanceResponse; +import comento.backend.ticket.repository.PerformanceRepository; +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.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; //몇 번 호출되었는지 검증 +import static org.mockito.Mockito.verify; //해당 기능이 사용되었는지 검증 + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(MockitoExtension.class) //JUnit5와 Mockito 연동 +public class PerformanceServiceTest2 { + + @Mock //mock객체로 생성 + private PerformanceRepository performanceRepository; + + private PerformanceService performanceService; + + @BeforeEach + void init() { + //실제 존재하는 performanceRepository가 주입되는것이 아닌 Mock PerformanceRepository가 주입 + performanceService = new PerformanceService(performanceRepository); + } + @Test + @DisplayName("[실패] 없는 날짜 정보 입력 - 404 NOT FOUND ERROR") + void 공연정보가_없으면_예외를_던진다2() throws ParseException { + //given + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = format.parse("2021-06-31"); + + //performanceRepository를 mock했으니 테스트 상황에서 원하는 결과를 새롭게 구현하는 코드. + //테스트가 돌아가는 동안에는 willReturn에서 반환하는 결과를 반드시 return하므로 PerformanceRepository를 의존하지 않고도 돌아가는 테스트코드가 완성 + given(performanceRepository.findByStartDateGreaterThanEqualOrderByStartDateAsc(startDate)) + .willReturn(new ArrayList()); + //when + //공연정보가 없는 경우 Service에서는 NOT FOUND ERROR 예외 호출 + NotFoundDataException nfde = assertThrows(NotFoundDataException.class, () -> { + performanceService.getListPerformance(new PerformanceDto(null, startDate)); + }); + //then + String msg = nfde.getMessage(); + assertThat(msg).isEqualTo(null); + } + //@TODO : 성공해야 하는데 두 테스트 메소드 모두 NotFoundDataException 발생,, + @Test + @DisplayName("[성공] 날짜를 정확히 입력한 경우") + void 공연정보가_있으면_응답한다1() throws ParseException { + //given + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = format.parse("2021-06-20"); + + given(performanceRepository.findByStartDateGreaterThanEqualOrderByStartDateAsc(startDate)) + .willReturn(new ArrayList<>()); + //when + List result = performanceService.getListPerformance(new PerformanceDto(null, startDate)); + + //then + assertThat(result.size()).isNotZero(); //2개 + } + @Test + @DisplayName("[성공] 날짜, 제목을 정확히 입력한 경우") + void 공연정보가_있으면_응답한다2() throws ParseException { + //given + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date startDate = format.parse("2021-06-20"); + String title = "국립무용단 <산조>"; + List temp = new ArrayList<>(); + given(performanceRepository.findByTitleAndStartDateGreaterThanEqualOrderByStartDate(title, startDate)) + .willReturn(temp); + //when + List result = performanceService.getListPerformance(new PerformanceDto(title, startDate)); + + //then + assertThat(result.size()).isNotZero(); + } +} \ No newline at end of file From 02d1288560e02de2d6b5be72c62573d3d731e2c2 Mon Sep 17 00:00:00 2001 From: hyejungg Date: Fri, 1 Oct 2021 11:57:47 +0900 Subject: [PATCH 6/8] =?UTF-8?q?[feat#11]=20=EA=B3=B5=EC=97=B0=20=EC=A2=8C?= =?UTF-8?q?=EC=84=9D=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C,=20?= =?UTF-8?q?=EA=B3=B5=EC=97=B0=20=EC=A2=8C=EC=84=9D=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?(=EB=8F=99=EC=8B=9C=EC=84=B1=20=ED=95=B4=EA=B2=B0=20X)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 공연 좌석 정보를 조회하는 코드 구현 2. 공연 좌석을 예약하는 코드 구현 2-1. 같은 좌석에 여러 명이 예약되는 문제가 있음. (해결하지 못함) --- .../ticket/controller/BookingController.java | 55 ++++++++++++++ .../controller/PerformanceController.java | 17 ++++- .../backend/ticket/domain/Booking.java | 48 ++++++++++++ .../backend/ticket/domain/BookingHistory.java | 51 +++++++++++++ .../comento/backend/ticket/domain/Seat.java | 43 +++++++++++ .../backend/ticket/dto/BookingDto.java | 68 +++++++++++++++++ .../backend/ticket/dto/BookingHistoryDto.java | 24 ++++++ .../backend/ticket/dto/BookingResponse.java | 26 +++++++ .../ticket/dto/BookingResponseCreated.java | 14 ++++ .../comento/backend/ticket/dto/SeatDto.java | 32 ++++++++ .../backend/ticket/dto/SeatResponse.java | 25 ++++++ .../comento/backend/ticket/dto/SeatType.java | 9 +++ .../repository/BookingHistoryRepository.java | 9 +++ .../ticket/repository/BookingRepository.java | 14 ++++ .../repository/PerformanceRepository.java | 2 + .../ticket/repository/SeatRepository.java | 15 ++++ .../ticket/service/BookingHistoryService.java | 20 +++++ .../ticket/service/BookingService.java | 51 +++++++++++++ .../ticket/service/PerformanceService.java | 6 ++ .../backend/ticket/service/SeatService.java | 45 +++++++++++ .../backend/ticket/service/UserService.java | 7 ++ .../ticket/repository/SeatRepositoryTest.java | 42 ++++++++++ ...2.java => MockPerformanceServiceTest.java} | 15 ++-- .../ticket/service/MockSeatServiceTest.java | 76 +++++++++++++++++++ 24 files changed, 706 insertions(+), 8 deletions(-) create mode 100644 ticket/src/main/java/comento/backend/ticket/controller/BookingController.java create mode 100644 ticket/src/main/java/comento/backend/ticket/domain/Booking.java create mode 100644 ticket/src/main/java/comento/backend/ticket/domain/BookingHistory.java create mode 100644 ticket/src/main/java/comento/backend/ticket/domain/Seat.java create mode 100644 ticket/src/main/java/comento/backend/ticket/dto/BookingDto.java create mode 100644 ticket/src/main/java/comento/backend/ticket/dto/BookingHistoryDto.java create mode 100644 ticket/src/main/java/comento/backend/ticket/dto/BookingResponse.java create mode 100644 ticket/src/main/java/comento/backend/ticket/dto/BookingResponseCreated.java create mode 100644 ticket/src/main/java/comento/backend/ticket/dto/SeatDto.java create mode 100644 ticket/src/main/java/comento/backend/ticket/dto/SeatResponse.java create mode 100644 ticket/src/main/java/comento/backend/ticket/dto/SeatType.java create mode 100644 ticket/src/main/java/comento/backend/ticket/repository/BookingHistoryRepository.java create mode 100644 ticket/src/main/java/comento/backend/ticket/repository/BookingRepository.java create mode 100644 ticket/src/main/java/comento/backend/ticket/repository/SeatRepository.java create mode 100644 ticket/src/main/java/comento/backend/ticket/service/BookingHistoryService.java create mode 100644 ticket/src/main/java/comento/backend/ticket/service/BookingService.java create mode 100644 ticket/src/main/java/comento/backend/ticket/service/SeatService.java create mode 100644 ticket/src/test/java/comento/backend/ticket/repository/SeatRepositoryTest.java rename ticket/src/test/java/comento/backend/ticket/service/{PerformanceServiceTest2.java => MockPerformanceServiceTest.java} (91%) create mode 100644 ticket/src/test/java/comento/backend/ticket/service/MockSeatServiceTest.java diff --git a/ticket/src/main/java/comento/backend/ticket/controller/BookingController.java b/ticket/src/main/java/comento/backend/ticket/controller/BookingController.java new file mode 100644 index 0000000..c57c55c --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/controller/BookingController.java @@ -0,0 +1,55 @@ +package comento.backend.ticket.controller; + +import comento.backend.ticket.config.SuccessCode; +import comento.backend.ticket.config.SuccessResponse; +import comento.backend.ticket.domain.Booking; +import comento.backend.ticket.dto.BookingDto; +import comento.backend.ticket.dto.BookingResponseCreated; +import comento.backend.ticket.dto.SeatDto; +import comento.backend.ticket.service.BookingHistoryService; +import comento.backend.ticket.service.BookingService; +import comento.backend.ticket.service.SeatService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@RestController +@RequestMapping("/api/performance/booking") +public class BookingController { + private final BookingService bookingService; + private final SeatService seatService; + private final BookingHistoryService bookingHistoryService; + private SuccessCode successCode; + + @Autowired + public BookingController(BookingService bookingService, SeatService seatService, BookingHistoryService bookingHistoryService) { + this.bookingService = bookingService; + this.seatService = seatService; + this.bookingHistoryService = bookingHistoryService; + } + + @PostMapping("") + public ResponseEntity addBooking(@Valid @RequestBody BookingDto reqBooking){ + Booking booking = bookingService.saveBookging(reqBooking); + if(booking == null){ + + } + + BookingResponseCreated result = new BookingResponseCreated(reqBooking.getSeatType(), reqBooking.getSeatNumber()); + successCode = SuccessCode.CREATED; + return new ResponseEntity(SuccessResponse.res(successCode.getStatus(), successCode.getMessage(), result), + HttpStatus.CREATED); + } + + //@TODO : 예약 정보 조회하기 +// @GetMapping("/email/{email}") +// public ResponseEntity showMyBooking(@Valid @PathVariable String email){ +// +// successCode = SuccessCode.OK; +// return new ResponseEntity(SuccessResponse.res(successCode.getStatus(), successCode.getMessage(), result) +// HttpStatus.OK); +// } +} diff --git a/ticket/src/main/java/comento/backend/ticket/controller/PerformanceController.java b/ticket/src/main/java/comento/backend/ticket/controller/PerformanceController.java index 437b745..a1c59be 100644 --- a/ticket/src/main/java/comento/backend/ticket/controller/PerformanceController.java +++ b/ticket/src/main/java/comento/backend/ticket/controller/PerformanceController.java @@ -5,8 +5,10 @@ import comento.backend.ticket.domain.Performance; import comento.backend.ticket.dto.PerformanceDto; import comento.backend.ticket.dto.PerformanceResponse; +import comento.backend.ticket.dto.SeatResponse; import comento.backend.ticket.service.PerformanceService; import comento.backend.ticket.service.SeatService; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpStatus; @@ -19,8 +21,6 @@ import javax.validation.Valid; import java.util.*; import java.util.Date; -import java.util.stream.Collectors; -import java.util.stream.Stream; @RestController @RequestMapping("/api/performance") @@ -45,4 +45,17 @@ public ResponseEntity showPerformanceInfo(@Valid @RequestParam(value = "date", r return new ResponseEntity(SuccessResponse.res(successCode.getStatus(), successCode.getMessage(), result), HttpStatus.OK); } + + @GetMapping("/info/seat") + public ResponseEntity showPerformanceSeatInfo(@Valid @RequestParam(value = "date", required = true) + @DateTimeFormat(pattern = "yyyy-MM-dd") Date date, + @Valid @RequestParam(value = "title", required = true) String title) { + PerformanceDto performanceDto = new PerformanceDto(title, date); + List performanceData = performanceService.getListPerformance(performanceDto); + + List seatResult = seatService.getListPerformanceSeat(performanceData.get(0)); + + return new ResponseEntity(SuccessResponse.res(successCode.getStatus(), successCode.getMessage(), seatResult), + HttpStatus.OK); + } } diff --git a/ticket/src/main/java/comento/backend/ticket/domain/Booking.java b/ticket/src/main/java/comento/backend/ticket/domain/Booking.java new file mode 100644 index 0000000..d92a41b --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/domain/Booking.java @@ -0,0 +1,48 @@ +package comento.backend.ticket.domain; + +import lombok.Builder; +import lombok.Data; +import org.hibernate.annotations.CreationTimestamp; + +import javax.persistence.*; +import java.util.Date; + +@Entity +@Data +@Table(name="Booking") +public class Booking { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="booking_id") + private Long id; + + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + + @ManyToOne + @JoinColumn(name = "performance_id") + private Performance performance; + + @ManyToOne + @JoinColumn(name = "seat_id") + private Seat seat; + + @Column(name="create_at") + @CreationTimestamp //Entity가 생성되어 저장될 때 시간이 자동 저장 + @Temporal(TemporalType.TIMESTAMP) + private Date createAt; + + public Booking(){} + + @Builder + public Booking(Long id, User user, Performance performance, Seat seat, Date createAt) { + this.id = id; + this.user = user; + this.performance = performance; + this.seat = seat; + this.createAt = createAt; + } +} + diff --git a/ticket/src/main/java/comento/backend/ticket/domain/BookingHistory.java b/ticket/src/main/java/comento/backend/ticket/domain/BookingHistory.java new file mode 100644 index 0000000..9abb125 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/domain/BookingHistory.java @@ -0,0 +1,51 @@ +package comento.backend.ticket.domain; + +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.CreationTimestamp; + +import javax.persistence.*; +import java.util.Date; + +@Entity +@Data +@Table(name="BookingHistory") +public class BookingHistory { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="bh_id") //booking_histroy id + private Long id; + + @Column(name = "is_success", columnDefinition = "boolean default false") + private Boolean isSuccess; + + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + + @ManyToOne + @JoinColumn(name = "performance_id") + private Performance performance; + + @ManyToOne + @JoinColumn(name = "seat_id") + private Seat seat; + + @Column(name="create_at") + @CreationTimestamp //Entity가 생성되어 저장될 때 시간이 자동 저장 + @Temporal(TemporalType.TIMESTAMP) + private Date createAt; + + public BookingHistory(){} + + @Builder + public BookingHistory(Boolean isSuccess, User user, Performance performance, Seat seat) { + this.isSuccess = isSuccess; + this.user = user; + this.performance = performance; + this.seat = seat; + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/domain/Seat.java b/ticket/src/main/java/comento/backend/ticket/domain/Seat.java new file mode 100644 index 0000000..2d64c37 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/domain/Seat.java @@ -0,0 +1,43 @@ +package comento.backend.ticket.domain; + +import comento.backend.ticket.dto.SeatType; +import lombok.Builder; +import lombok.Data; + +import javax.persistence.*; + +@Entity +@Data +@Table(name = "Seat") +public class Seat { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "seat_id") + private Long id; + + @ManyToOne + @JoinColumn(name = "performance_id") + private Performance performance; + + @Column(name = "seat_type", nullable = true) + @Enumerated(EnumType.STRING) //enum 타입으로 DB에 저장 + private SeatType seatType; + + @Column(name = "seat_number", nullable = true) + private Integer seatNumber; + + @Column(name = "is_booking", columnDefinition = "boolean default false") + private boolean isBooking; + + public Seat(){} + + @Builder + public Seat(Long id, Performance performance, SeatType seatType, Integer seatNumber, boolean isBooking) { + this.id = id; + this.performance = performance; + this.seatType = seatType; + this.seatNumber = seatNumber; + this.isBooking = isBooking; + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/dto/BookingDto.java b/ticket/src/main/java/comento/backend/ticket/dto/BookingDto.java new file mode 100644 index 0000000..ff803d3 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/dto/BookingDto.java @@ -0,0 +1,68 @@ +package comento.backend.ticket.dto; + +import comento.backend.ticket.domain.Booking; +import comento.backend.ticket.domain.Performance; +import comento.backend.ticket.domain.Seat; +import comento.backend.ticket.domain.User; +import lombok.Data; +import lombok.Getter; + +import java.util.Date; + +/* + "date" : 2021-09-04, + "title" : "뮤지컬", + "email" :"kimhyejung12@naver.com" + "seat" : "VIP석" + "seat_number" : 2 + */ + +@Getter +@Data +public class BookingDto { + private Long id; + private String title; + private Date startDate; + private String email; + private SeatType seatType; + private Integer seatNumber; + private String price; + private User user; + private Performance performance; + private Seat seat; + + public BookingDto() { + } + + public BookingDto(Long id, String title, Date startDate, String email, SeatType seatType, Integer seatNumber, String price) { + this.id = id; + this.title = title; + this.startDate = startDate; + this.email = email; + this.seatType = seatType; + this.seatNumber = seatNumber; + this.price = price; + } + + public BookingDto(Long id, String title, Date startDate, String email, SeatType seatType, Integer seatNumber, String price, User user, Performance performance, Seat seat) { + this.id = id; + this.title = title; + this.startDate = startDate; + this.email = email; + this.seatType = seatType; + this.seatNumber = seatNumber; + this.price = price; + this.user = user; + this.performance = performance; + this.seat = seat; + } + + public Booking toEntity() { + return Booking.builder() + .user(user) + .performance(performance) + .seat(seat) + .build(); + } + +} \ No newline at end of file diff --git a/ticket/src/main/java/comento/backend/ticket/dto/BookingHistoryDto.java b/ticket/src/main/java/comento/backend/ticket/dto/BookingHistoryDto.java new file mode 100644 index 0000000..c9a97b7 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/dto/BookingHistoryDto.java @@ -0,0 +1,24 @@ +package comento.backend.ticket.dto; + +import comento.backend.ticket.domain.BookingHistory; +import comento.backend.ticket.domain.Performance; +import comento.backend.ticket.domain.Seat; +import comento.backend.ticket.domain.User; +import lombok.Data; + +@Data +public class BookingHistoryDto { + private boolean isSuccess; + private User user; + private Performance performance; + private Seat seat; + + public BookingHistory toEntity() { + return BookingHistory.builder() + .isSuccess(isSuccess) + .user(user) + .performance(performance) + .seat(seat) + .build(); + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/dto/BookingResponse.java b/ticket/src/main/java/comento/backend/ticket/dto/BookingResponse.java new file mode 100644 index 0000000..ab399fb --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/dto/BookingResponse.java @@ -0,0 +1,26 @@ +package comento.backend.ticket.dto; + +import lombok.Data; + +import java.util.Date; + +@Data +public class BookingResponse { + private String title; + private Date startDate; + private String email; + private SeatType seatType; + private Integer seatNumber; + private String price; + + public BookingResponse(){} + + public BookingResponse(String title, Date startDate, String email, SeatType seatType, Integer seatNumber, String price) { + this.title = title; + this.startDate = startDate; + this.email = email; + this.seatType = seatType; + this.seatNumber = seatNumber; + this.price = price; + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/dto/BookingResponseCreated.java b/ticket/src/main/java/comento/backend/ticket/dto/BookingResponseCreated.java new file mode 100644 index 0000000..71d70e0 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/dto/BookingResponseCreated.java @@ -0,0 +1,14 @@ +package comento.backend.ticket.dto; + +import lombok.Data; + +@Data +public class BookingResponseCreated { + private SeatType seatType; + private Integer seatNumber; + + public BookingResponseCreated(SeatType seatType, Integer seatNumber) { + this.seatType = seatType; + this.seatNumber = seatNumber; + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/dto/SeatDto.java b/ticket/src/main/java/comento/backend/ticket/dto/SeatDto.java new file mode 100644 index 0000000..e907c15 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/dto/SeatDto.java @@ -0,0 +1,32 @@ +package comento.backend.ticket.dto; + +import comento.backend.ticket.domain.Performance; +import comento.backend.ticket.domain.Seat; +import lombok.Data; + +@Data +public class SeatDto { + private Long seat_id; + private Performance performance; + private SeatType seatType; + private Integer seatNumber; + private boolean isBooking; + + public SeatDto(Long seat_id, Performance performance, SeatType seatType, Integer seatNumber, boolean isBooking) { + this.seat_id = seat_id; + this.performance = performance; + this.seatType = seatType; + this.seatNumber = seatNumber; + this.isBooking = isBooking; + } + + public Seat toEntity(){ + return Seat.builder() + .id(seat_id) + .performance(performance) + .seatType(seatType) + .seatNumber(seatNumber) + .isBooking(isBooking) + .build(); + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/dto/SeatResponse.java b/ticket/src/main/java/comento/backend/ticket/dto/SeatResponse.java new file mode 100644 index 0000000..85d6992 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/dto/SeatResponse.java @@ -0,0 +1,25 @@ +package comento.backend.ticket.dto; + +import comento.backend.ticket.domain.Seat; +import lombok.Data; + +@Data +public class SeatResponse { + private SeatType seatType; + private Integer seatNumber; + private boolean isBooking; + + public SeatResponse(SeatType seatType, Integer seatNumber, boolean isBooking) { + this.seatType = seatType; + this.seatNumber = seatNumber; + this.isBooking = isBooking; + } + + public static SeatResponse of(Seat seat){ + return new SeatResponse( + seat.getSeatType(), + seat.getSeatNumber(), + seat.isBooking() + ); + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/dto/SeatType.java b/ticket/src/main/java/comento/backend/ticket/dto/SeatType.java new file mode 100644 index 0000000..33b9127 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/dto/SeatType.java @@ -0,0 +1,9 @@ +package comento.backend.ticket.dto; + +public enum SeatType { + VIP, + S, + A, + B, + C +} diff --git a/ticket/src/main/java/comento/backend/ticket/repository/BookingHistoryRepository.java b/ticket/src/main/java/comento/backend/ticket/repository/BookingHistoryRepository.java new file mode 100644 index 0000000..bac48e6 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/repository/BookingHistoryRepository.java @@ -0,0 +1,9 @@ +package comento.backend.ticket.repository; + +import comento.backend.ticket.domain.BookingHistory; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface BookingHistoryRepository extends JpaRepository { +} diff --git a/ticket/src/main/java/comento/backend/ticket/repository/BookingRepository.java b/ticket/src/main/java/comento/backend/ticket/repository/BookingRepository.java new file mode 100644 index 0000000..3306d56 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/repository/BookingRepository.java @@ -0,0 +1,14 @@ +package comento.backend.ticket.repository; + +import comento.backend.ticket.domain.Booking; +import comento.backend.ticket.domain.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface BookingRepository extends JpaRepository { + + Optional findByUser(User user); +} diff --git a/ticket/src/main/java/comento/backend/ticket/repository/PerformanceRepository.java b/ticket/src/main/java/comento/backend/ticket/repository/PerformanceRepository.java index 45b2869..1d6f872 100644 --- a/ticket/src/main/java/comento/backend/ticket/repository/PerformanceRepository.java +++ b/ticket/src/main/java/comento/backend/ticket/repository/PerformanceRepository.java @@ -6,9 +6,11 @@ import java.util.Date; import java.util.List; +import java.util.Optional; @Repository public interface PerformanceRepository extends JpaRepository { List findByStartDateGreaterThanEqualOrderByStartDateAsc(Date startDate); List findByTitleAndStartDateGreaterThanEqualOrderByStartDate(String title, Date startDate); + Optional findByIdAndTitle(Long id, String title); } diff --git a/ticket/src/main/java/comento/backend/ticket/repository/SeatRepository.java b/ticket/src/main/java/comento/backend/ticket/repository/SeatRepository.java new file mode 100644 index 0000000..0a55b4e --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/repository/SeatRepository.java @@ -0,0 +1,15 @@ +package comento.backend.ticket.repository; + +import comento.backend.ticket.domain.Performance; +import comento.backend.ticket.domain.Seat; +import comento.backend.ticket.dto.SeatType; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.*; + +@Repository +public interface SeatRepository extends JpaRepository { + List findByPerformance(Performance performance); + Optional findByPerformanceAndSeatTypeAndSeatNumber(Performance performance, SeatType seatType, Integer seatNumber); +} diff --git a/ticket/src/main/java/comento/backend/ticket/service/BookingHistoryService.java b/ticket/src/main/java/comento/backend/ticket/service/BookingHistoryService.java new file mode 100644 index 0000000..e57f422 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/service/BookingHistoryService.java @@ -0,0 +1,20 @@ +package comento.backend.ticket.service; + +import comento.backend.ticket.domain.BookingHistory; +import comento.backend.ticket.dto.BookingHistoryDto; +import comento.backend.ticket.repository.BookingHistoryRepository; +import org.springframework.stereotype.Service; + +@Service +public class BookingHistoryService { + private final BookingHistoryRepository bookingHistoryRepository; + + public BookingHistoryService(BookingHistoryRepository bookingHistoryRepository) { + this.bookingHistoryRepository = bookingHistoryRepository; + } + + public BookingHistory saveBookingHistory(BookingHistoryDto bookingHistoryDto){ + BookingHistory bookingHistory = bookingHistoryDto.toEntity(); + return bookingHistoryRepository.save(bookingHistory); + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/service/BookingService.java b/ticket/src/main/java/comento/backend/ticket/service/BookingService.java new file mode 100644 index 0000000..f13e55c --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/service/BookingService.java @@ -0,0 +1,51 @@ +package comento.backend.ticket.service; + +import comento.backend.ticket.domain.Booking; +import comento.backend.ticket.domain.Performance; +import comento.backend.ticket.domain.Seat; +import comento.backend.ticket.domain.User; +import comento.backend.ticket.dto.BookingDto; +import comento.backend.ticket.dto.SeatDto; +import comento.backend.ticket.repository.BookingRepository; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +public class BookingService { + private final BookingRepository bookingRepository; + private final PerformanceService performanceService; + private final UserService userService; + private final SeatService seatService; + + public BookingService(BookingRepository bookingRepository, PerformanceService performanceService, UserService userService, SeatService seatService) { + this.bookingRepository = bookingRepository; + this.performanceService = performanceService; + this.userService = userService; + this.seatService = seatService; + } + + // @TODO : VIP, 3이 여러 개 저장되는 이슈 해결 해야 함 (동시성 문제) + //예약 정보 저장하고, Seat 정보에는 is_booking을 true로 변경 + public Booking saveBookging(BookingDto reqBooking){ + User user = userService.getUser(reqBooking.getEmail()); + Performance performance = performanceService.getPerformance(reqBooking.getId(), reqBooking.getTitle()); + Seat seat = seatService.getSeat(performance, reqBooking.getSeatType(), reqBooking.getSeatNumber()); + + reqBooking.setUser(user); + reqBooking.setPerformance(performance); + reqBooking.setSeat(seat); + + //seat 테이블의 is_booking 칼럼을 true로 update + SeatDto seatDto = new SeatDto(seat.getId(), performance, reqBooking.getSeatType(), reqBooking.getSeatNumber(), true); + seatService.updateSeat(seatDto); + + Booking booking = reqBooking.toEntity(); + return bookingRepository.save(booking); + } + + public Optional getMyBooking(String email){ + + return null; + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java b/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java index 9afa603..431f868 100644 --- a/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java +++ b/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Date; +import java.util.Optional; import java.util.stream.Collectors; @Service @@ -30,4 +31,9 @@ public List getListPerformance(PerformanceDto performanceDt } return result.stream().map(PerformanceResponse::of).collect(Collectors.toList()); } + + public Performance getPerformance(Long id, String title){ + Optional performance = performanceRepository.findByIdAndTitle(id, title); + return performance.orElseThrow(NotFoundDataException::new); + } } diff --git a/ticket/src/main/java/comento/backend/ticket/service/SeatService.java b/ticket/src/main/java/comento/backend/ticket/service/SeatService.java new file mode 100644 index 0000000..22a0756 --- /dev/null +++ b/ticket/src/main/java/comento/backend/ticket/service/SeatService.java @@ -0,0 +1,45 @@ +package comento.backend.ticket.service; + +import comento.backend.ticket.config.customException.NotFoundDataException; +import comento.backend.ticket.domain.Performance; +import comento.backend.ticket.domain.Seat; +import comento.backend.ticket.dto.PerformanceResponse; +import comento.backend.ticket.dto.SeatDto; +import comento.backend.ticket.dto.SeatResponse; +import comento.backend.ticket.dto.SeatType; +import comento.backend.ticket.repository.SeatRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +public class SeatService { + private final SeatRepository seatRepository; + + public SeatService(SeatRepository seatRepository) { + this.seatRepository = seatRepository; + } + + public List getListPerformanceSeat(PerformanceResponse performanceData) { + Performance performance = new Performance(); + performance.setId(performanceData.getId()); + List seatResult = seatRepository.findByPerformance(performance); + if(seatResult.isEmpty()){ + throw new NotFoundDataException(); + } + return seatResult.stream().map(SeatResponse::of).collect(Collectors.toList()); + } + + public Seat getSeat(Performance performance, SeatType seatType, Integer seatNumber) { + Optional seat = seatRepository.findByPerformanceAndSeatTypeAndSeatNumber(performance, seatType, seatNumber); + return seat.orElseThrow(NotFoundDataException::new); + } + + public Seat updateSeat(SeatDto seatDto) { + Seat seat = seatDto.toEntity(); + return seatRepository.save(seat); + } +} diff --git a/ticket/src/main/java/comento/backend/ticket/service/UserService.java b/ticket/src/main/java/comento/backend/ticket/service/UserService.java index 631ea57..1cb1112 100644 --- a/ticket/src/main/java/comento/backend/ticket/service/UserService.java +++ b/ticket/src/main/java/comento/backend/ticket/service/UserService.java @@ -1,12 +1,14 @@ package comento.backend.ticket.service; import comento.backend.ticket.config.customException.DuplicatedException; +import comento.backend.ticket.config.customException.NoAuthException; import comento.backend.ticket.domain.User; import comento.backend.ticket.dto.UserDto; import comento.backend.ticket.repository.UserRepository; import org.springframework.stereotype.Service; import java.util.Objects; +import java.util.Optional; @Service @@ -31,4 +33,9 @@ private boolean checkEmailDuplicate(String email){ User user = userRepository.findByEmail(email).orElseGet(()->null); return Objects.isNull(user); //null이면 true } + + public User getUser(String email){ + Optional user = userRepository.findByEmail(email); + return user.orElseThrow(NoAuthException::new); + } } diff --git a/ticket/src/test/java/comento/backend/ticket/repository/SeatRepositoryTest.java b/ticket/src/test/java/comento/backend/ticket/repository/SeatRepositoryTest.java new file mode 100644 index 0000000..611e9af --- /dev/null +++ b/ticket/src/test/java/comento/backend/ticket/repository/SeatRepositoryTest.java @@ -0,0 +1,42 @@ +package comento.backend.ticket.repository; + +import comento.backend.ticket.domain.Performance; +import comento.backend.ticket.domain.Seat; +import comento.backend.ticket.dto.SeatType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@Transactional +public class SeatRepositoryTest { + private final SeatRepository seatRepository; + + @Autowired + public SeatRepositoryTest(SeatRepository seatRepository) { + this.seatRepository = seatRepository; + } + + @Test + @DisplayName("조회를 성공한다면 ID값을 가져온다.") + void seat_정보_조회() throws ParseException { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date date = format.parse("2021-06-20"); + Performance performance = new Performance(1L, "2021 정오의 음악회 5월", "국악", date, date, + "전석 20,000원", "국립극장\n하늘극장\n8세 이상 관람가", "70분"); + + Optional result = seatRepository.findByPerformanceAndSeatTypeAndSeatNumber(performance, SeatType.VIP, 2); + + assertThat(result.get().getId()).isNotZero(); + } +} diff --git a/ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest2.java b/ticket/src/test/java/comento/backend/ticket/service/MockPerformanceServiceTest.java similarity index 91% rename from ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest2.java rename to ticket/src/test/java/comento/backend/ticket/service/MockPerformanceServiceTest.java index 6030887..60fee9f 100644 --- a/ticket/src/test/java/comento/backend/ticket/service/PerformanceServiceTest2.java +++ b/ticket/src/test/java/comento/backend/ticket/service/MockPerformanceServiceTest.java @@ -11,6 +11,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.transaction.annotation.Transactional; import java.text.DateFormat; import java.text.ParseException; @@ -24,8 +25,9 @@ import static org.assertj.core.api.Assertions.assertThat; +@Transactional @ExtendWith(MockitoExtension.class) //JUnit5와 Mockito 연동 -public class PerformanceServiceTest2 { +public class MockPerformanceServiceTest { @Mock //mock객체로 생성 private PerformanceRepository performanceRepository; @@ -55,9 +57,8 @@ void init() { }); //then String msg = nfde.getMessage(); - assertThat(msg).isEqualTo(null); + assertThat(msg).isNull(); } - //@TODO : 성공해야 하는데 두 테스트 메소드 모두 NotFoundDataException 발생,, @Test @DisplayName("[성공] 날짜를 정확히 입력한 경우") void 공연정보가_있으면_응답한다1() throws ParseException { @@ -71,6 +72,7 @@ void init() { List result = performanceService.getListPerformance(new PerformanceDto(null, startDate)); //then + System.out.println(result); assertThat(result.size()).isNotZero(); //2개 } @Test @@ -80,13 +82,14 @@ void init() { DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date startDate = format.parse("2021-06-20"); String title = "국립무용단 <산조>"; - List temp = new ArrayList<>(); + PerformanceDto dto = new PerformanceDto(title, startDate); given(performanceRepository.findByTitleAndStartDateGreaterThanEqualOrderByStartDate(title, startDate)) - .willReturn(temp); + .willReturn(new ArrayList<>()); //when - List result = performanceService.getListPerformance(new PerformanceDto(title, startDate)); + List result = performanceService.getListPerformance(dto); //then + System.out.println(result); assertThat(result.size()).isNotZero(); } } \ No newline at end of file diff --git a/ticket/src/test/java/comento/backend/ticket/service/MockSeatServiceTest.java b/ticket/src/test/java/comento/backend/ticket/service/MockSeatServiceTest.java new file mode 100644 index 0000000..4e6d62a --- /dev/null +++ b/ticket/src/test/java/comento/backend/ticket/service/MockSeatServiceTest.java @@ -0,0 +1,76 @@ +package comento.backend.ticket.service; + +import comento.backend.ticket.config.customException.NotFoundDataException; +import comento.backend.ticket.domain.Performance; +import comento.backend.ticket.domain.Seat; +import comento.backend.ticket.dto.PerformanceDto; +import comento.backend.ticket.dto.PerformanceResponse; +import comento.backend.ticket.dto.SeatResponse; +import comento.backend.ticket.repository.SeatRepository; +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.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.transaction.annotation.Transactional; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.BDDMockito.given; + +import static org.assertj.core.api.Assertions.assertThat; + +@Transactional +@ExtendWith(MockitoExtension.class) +public class MockSeatServiceTest { + @Mock //mock객체로 생성 + private SeatRepository seatRepository; + + private SeatService seatService; + + @BeforeEach + void init() { + seatService = new SeatService(seatRepository); //mock 주입 + } + @Test + @DisplayName("[실패] 404 NOT FOUND ERROR") + void 좌석_정보가_없으면_예외를_던진다() { + //given + Long id = 1L; + + given(seatRepository.findByPerformanceId(id)) + .willReturn(new ArrayList()); + //when + NotFoundDataException nfde = assertThrows(NotFoundDataException.class, () -> { + seatService.getListPerformanceSeat(id); + }); + //then + String msg = nfde.getMessage(); + assertThat(msg).isEqualTo(null); + } + @Test + @DisplayName("[성공]") + void 좌석_정보를_보여준다() throws ParseException { + //given + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + Date date = format.parse("2021-06-20"); + PerformanceResponse performanceResponse = new PerformanceResponse(1L, "2021 정오의 음악회 5월", date, date, + "전석 20,000원", "국악", "국립극장\n하늘극장\n8세 이상 관람가", "70분"); + + Performance performance = new Performance(); + performance.setId(performanceResponse.getId()); + + given(seatRepository.findByPerformance(performance)) + .willReturn(new ArrayList()); + //when + List list = seatService.getListPerformanceSeat(performanceResponse); + + //then + assertThat(list.size()).isNotZero(); + } +} From 05d86607cba1fded0eafd722164f4e2cd218e66f Mon Sep 17 00:00:00 2001 From: hyejungg Date: Sat, 2 Oct 2021 20:18:22 +0900 Subject: [PATCH 7/8] =?UTF-8?q?[fix:11]=20=ED=94=BC=EB=93=9C=EB=B0=B1=20?= =?UTF-8?q?=EC=88=98=EC=A0=95,=20=EB=82=98=EC=9D=98=20=EA=B3=B5=EC=97=B0?= =?UTF-8?q?=20=EC=A2=8C=EC=84=9D=20=EC=98=88=EC=95=BD=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=B6=94=EA=B0=80,=20=EC=98=88=EC=95=BD?= =?UTF-8?q?=20=EC=84=B1=EA=B3=B5/=EC=8B=A4=ED=8C=A8=20=EC=97=AC=EB=B6=80?= =?UTF-8?q?=20=EC=A0=80=EC=9E=A5=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 모든 서비스 계층에서 받는 매개변수는 final 변수로 수정하였음 - 서비스 계층에서 가져오는 해당 값은 DB에 접근하기 위함으로 사용되는 것이니까 변경할 이유는 없다고 생각되고, 기존 코드 중에서도 변경되는 부분이 없음. - final로 선언하여 변경 불가 하도록 상수화 2. 나의 공연 좌석 예약 정보를 조회하는 코드 추가 3. 예약 성공/실패 여부 저장 코드 추가 - 예약 성공 시에는 저장되지만, 실패 시 저장되지 않는 문제가 있음. (해결해야 함) --- .../ticket/controller/BookingController.java | 32 +++----- .../backend/ticket/domain/Booking.java | 6 +- .../backend/ticket/domain/BookingHistory.java | 6 +- .../comento/backend/ticket/domain/Seat.java | 2 +- .../backend/ticket/dto/BookingDto.java | 31 +------- .../backend/ticket/dto/BookingHistoryDto.java | 13 ++++ .../comento/backend/ticket/dto/SeatDto.java | 4 + .../ticket/repository/BookingRepository.java | 14 +++- .../ticket/repository/SeatRepository.java | 2 + .../ticket/service/BookingHistoryService.java | 2 +- .../ticket/service/BookingService.java | 76 +++++++++++++------ .../ticket/service/PerformanceService.java | 4 +- .../backend/ticket/service/SeatService.java | 45 +++++++---- .../backend/ticket/service/UserService.java | 6 +- .../service/MockPerformanceServiceTest.java | 46 +++++++++-- .../ticket/service/MockSeatServiceTest.java | 35 +++++++-- 16 files changed, 205 insertions(+), 119 deletions(-) diff --git a/ticket/src/main/java/comento/backend/ticket/controller/BookingController.java b/ticket/src/main/java/comento/backend/ticket/controller/BookingController.java index c57c55c..81a846c 100644 --- a/ticket/src/main/java/comento/backend/ticket/controller/BookingController.java +++ b/ticket/src/main/java/comento/backend/ticket/controller/BookingController.java @@ -4,52 +4,42 @@ import comento.backend.ticket.config.SuccessResponse; import comento.backend.ticket.domain.Booking; import comento.backend.ticket.dto.BookingDto; +import comento.backend.ticket.dto.BookingResponse; import comento.backend.ticket.dto.BookingResponseCreated; -import comento.backend.ticket.dto.SeatDto; -import comento.backend.ticket.service.BookingHistoryService; import comento.backend.ticket.service.BookingService; -import comento.backend.ticket.service.SeatService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; +import java.util.*; @RestController @RequestMapping("/api/performance/booking") public class BookingController { private final BookingService bookingService; - private final SeatService seatService; - private final BookingHistoryService bookingHistoryService; private SuccessCode successCode; @Autowired - public BookingController(BookingService bookingService, SeatService seatService, BookingHistoryService bookingHistoryService) { + public BookingController(BookingService bookingService) { this.bookingService = bookingService; - this.seatService = seatService; - this.bookingHistoryService = bookingHistoryService; } @PostMapping("") public ResponseEntity addBooking(@Valid @RequestBody BookingDto reqBooking){ - Booking booking = bookingService.saveBookging(reqBooking); - if(booking == null){ - - } - + bookingService.saveBookging(reqBooking); BookingResponseCreated result = new BookingResponseCreated(reqBooking.getSeatType(), reqBooking.getSeatNumber()); successCode = SuccessCode.CREATED; return new ResponseEntity(SuccessResponse.res(successCode.getStatus(), successCode.getMessage(), result), HttpStatus.CREATED); } - //@TODO : 예약 정보 조회하기 -// @GetMapping("/email/{email}") -// public ResponseEntity showMyBooking(@Valid @PathVariable String email){ -// -// successCode = SuccessCode.OK; -// return new ResponseEntity(SuccessResponse.res(successCode.getStatus(), successCode.getMessage(), result) -// HttpStatus.OK); -// } + @GetMapping("/email/{email}") + public ResponseEntity showMyBooking(@Valid @PathVariable String email){ + List result = bookingService.getMyBooking(email); + successCode = SuccessCode.OK; + return new ResponseEntity(SuccessResponse.res(successCode.getStatus(), successCode.getMessage(), result), + HttpStatus.OK); + } } diff --git a/ticket/src/main/java/comento/backend/ticket/domain/Booking.java b/ticket/src/main/java/comento/backend/ticket/domain/Booking.java index d92a41b..4c1b0e8 100644 --- a/ticket/src/main/java/comento/backend/ticket/domain/Booking.java +++ b/ticket/src/main/java/comento/backend/ticket/domain/Booking.java @@ -17,15 +17,15 @@ public class Booking { @Column(name="booking_id") private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "performance_id") private Performance performance; - @ManyToOne + @OneToOne @JoinColumn(name = "seat_id") private Seat seat; diff --git a/ticket/src/main/java/comento/backend/ticket/domain/BookingHistory.java b/ticket/src/main/java/comento/backend/ticket/domain/BookingHistory.java index 9abb125..8a71a43 100644 --- a/ticket/src/main/java/comento/backend/ticket/domain/BookingHistory.java +++ b/ticket/src/main/java/comento/backend/ticket/domain/BookingHistory.java @@ -2,8 +2,6 @@ import lombok.Builder; import lombok.Data; -import lombok.Getter; -import lombok.Setter; import org.hibernate.annotations.CreationTimestamp; import javax.persistence.*; @@ -26,11 +24,11 @@ public class BookingHistory { @JoinColumn(name = "user_id") private User user; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "performance_id") private Performance performance; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "seat_id") private Seat seat; diff --git a/ticket/src/main/java/comento/backend/ticket/domain/Seat.java b/ticket/src/main/java/comento/backend/ticket/domain/Seat.java index 2d64c37..b5087ca 100644 --- a/ticket/src/main/java/comento/backend/ticket/domain/Seat.java +++ b/ticket/src/main/java/comento/backend/ticket/domain/Seat.java @@ -16,7 +16,7 @@ public class Seat { @Column(name = "seat_id") private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "performance_id") private Performance performance; diff --git a/ticket/src/main/java/comento/backend/ticket/dto/BookingDto.java b/ticket/src/main/java/comento/backend/ticket/dto/BookingDto.java index ff803d3..2a7ff98 100644 --- a/ticket/src/main/java/comento/backend/ticket/dto/BookingDto.java +++ b/ticket/src/main/java/comento/backend/ticket/dto/BookingDto.java @@ -27,37 +27,8 @@ public class BookingDto { private SeatType seatType; private Integer seatNumber; private String price; - private User user; - private Performance performance; - private Seat seat; - public BookingDto() { - } - - public BookingDto(Long id, String title, Date startDate, String email, SeatType seatType, Integer seatNumber, String price) { - this.id = id; - this.title = title; - this.startDate = startDate; - this.email = email; - this.seatType = seatType; - this.seatNumber = seatNumber; - this.price = price; - } - - public BookingDto(Long id, String title, Date startDate, String email, SeatType seatType, Integer seatNumber, String price, User user, Performance performance, Seat seat) { - this.id = id; - this.title = title; - this.startDate = startDate; - this.email = email; - this.seatType = seatType; - this.seatNumber = seatNumber; - this.price = price; - this.user = user; - this.performance = performance; - this.seat = seat; - } - - public Booking toEntity() { + public Booking toEntity(final User user, final Performance performance, final Seat seat) { return Booking.builder() .user(user) .performance(performance) diff --git a/ticket/src/main/java/comento/backend/ticket/dto/BookingHistoryDto.java b/ticket/src/main/java/comento/backend/ticket/dto/BookingHistoryDto.java index c9a97b7..3b8da8d 100644 --- a/ticket/src/main/java/comento/backend/ticket/dto/BookingHistoryDto.java +++ b/ticket/src/main/java/comento/backend/ticket/dto/BookingHistoryDto.java @@ -4,15 +4,28 @@ import comento.backend.ticket.domain.Performance; import comento.backend.ticket.domain.Seat; import comento.backend.ticket.domain.User; +import lombok.Builder; import lombok.Data; @Data public class BookingHistoryDto { + private Long id; private boolean isSuccess; private User user; private Performance performance; private Seat seat; + public BookingHistoryDto(){} + + @Builder + public BookingHistoryDto(Long id, boolean isSuccess, User user, Performance performance, Seat seat) { + this.id = id; + this.isSuccess = isSuccess; + this.user = user; + this.performance = performance; + this.seat = seat; + } + public BookingHistory toEntity() { return BookingHistory.builder() .isSuccess(isSuccess) diff --git a/ticket/src/main/java/comento/backend/ticket/dto/SeatDto.java b/ticket/src/main/java/comento/backend/ticket/dto/SeatDto.java index e907c15..0006163 100644 --- a/ticket/src/main/java/comento/backend/ticket/dto/SeatDto.java +++ b/ticket/src/main/java/comento/backend/ticket/dto/SeatDto.java @@ -2,6 +2,7 @@ import comento.backend.ticket.domain.Performance; import comento.backend.ticket.domain.Seat; +import lombok.Builder; import lombok.Data; @Data @@ -12,6 +13,9 @@ public class SeatDto { private Integer seatNumber; private boolean isBooking; + public SeatDto(){}; + + @Builder public SeatDto(Long seat_id, Performance performance, SeatType seatType, Integer seatNumber, boolean isBooking) { this.seat_id = seat_id; this.performance = performance; diff --git a/ticket/src/main/java/comento/backend/ticket/repository/BookingRepository.java b/ticket/src/main/java/comento/backend/ticket/repository/BookingRepository.java index 3306d56..bec1ad5 100644 --- a/ticket/src/main/java/comento/backend/ticket/repository/BookingRepository.java +++ b/ticket/src/main/java/comento/backend/ticket/repository/BookingRepository.java @@ -1,14 +1,20 @@ package comento.backend.ticket.repository; import comento.backend.ticket.domain.Booking; -import comento.backend.ticket.domain.User; +import comento.backend.ticket.dto.BookingResponse; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; -import java.util.Optional; +import java.util.*; @Repository public interface BookingRepository extends JpaRepository { - - Optional findByUser(User user); + /*해당 유저(u, ~@~)가 예약(b)한 공연 정보(p)와 좌석 정보(s) 조회*/ + @Query("select new comento.backend.ticket.dto.BookingResponse(p.title, p.startDate, u.email, s.seatType, s.seatNumber, p.price) " + + "from Seat s join s.performance p on s.performance = p.id " + + "join Booking b on b.seat = s.id " + + "join User u on u.id = b.user " + + "where s.isBooking = :isBooking and u.email = :email") + List findMyBooking(boolean isBooking, String email); } diff --git a/ticket/src/main/java/comento/backend/ticket/repository/SeatRepository.java b/ticket/src/main/java/comento/backend/ticket/repository/SeatRepository.java index 0a55b4e..0112f4e 100644 --- a/ticket/src/main/java/comento/backend/ticket/repository/SeatRepository.java +++ b/ticket/src/main/java/comento/backend/ticket/repository/SeatRepository.java @@ -12,4 +12,6 @@ public interface SeatRepository extends JpaRepository { List findByPerformance(Performance performance); Optional findByPerformanceAndSeatTypeAndSeatNumber(Performance performance, SeatType seatType, Integer seatNumber); + //isBooking 여부 확인을 위해 사용 + Optional findByPerformanceAndSeatTypeAndSeatNumberAndIsBooking(Performance performance, SeatType seatType, Integer seatNumber, boolean isBooking); } diff --git a/ticket/src/main/java/comento/backend/ticket/service/BookingHistoryService.java b/ticket/src/main/java/comento/backend/ticket/service/BookingHistoryService.java index e57f422..a4d74d9 100644 --- a/ticket/src/main/java/comento/backend/ticket/service/BookingHistoryService.java +++ b/ticket/src/main/java/comento/backend/ticket/service/BookingHistoryService.java @@ -13,7 +13,7 @@ public BookingHistoryService(BookingHistoryRepository bookingHistoryRepository) this.bookingHistoryRepository = bookingHistoryRepository; } - public BookingHistory saveBookingHistory(BookingHistoryDto bookingHistoryDto){ + public BookingHistory saveBookingHistory(final BookingHistoryDto bookingHistoryDto){ BookingHistory bookingHistory = bookingHistoryDto.toEntity(); return bookingHistoryRepository.save(bookingHistory); } diff --git a/ticket/src/main/java/comento/backend/ticket/service/BookingService.java b/ticket/src/main/java/comento/backend/ticket/service/BookingService.java index f13e55c..56b671e 100644 --- a/ticket/src/main/java/comento/backend/ticket/service/BookingService.java +++ b/ticket/src/main/java/comento/backend/ticket/service/BookingService.java @@ -1,51 +1,81 @@ package comento.backend.ticket.service; -import comento.backend.ticket.domain.Booking; -import comento.backend.ticket.domain.Performance; -import comento.backend.ticket.domain.Seat; -import comento.backend.ticket.domain.User; -import comento.backend.ticket.dto.BookingDto; -import comento.backend.ticket.dto.SeatDto; +import comento.backend.ticket.config.customException.NotFoundDataException; +import comento.backend.ticket.domain.*; +import comento.backend.ticket.dto.*; import comento.backend.ticket.repository.BookingRepository; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; @Service public class BookingService { private final BookingRepository bookingRepository; private final PerformanceService performanceService; + private final BookingHistoryService bookingHistoryService; private final UserService userService; private final SeatService seatService; - public BookingService(BookingRepository bookingRepository, PerformanceService performanceService, UserService userService, SeatService seatService) { + public BookingService(BookingRepository bookingRepository, PerformanceService performanceService, + BookingHistoryService bookingHistoryService, UserService userService, + SeatService seatService) { this.bookingRepository = bookingRepository; this.performanceService = performanceService; + this.bookingHistoryService = bookingHistoryService; this.userService = userService; this.seatService = seatService; } - // @TODO : VIP, 3이 여러 개 저장되는 이슈 해결 해야 함 (동시성 문제) - //예약 정보 저장하고, Seat 정보에는 is_booking을 true로 변경 - public Booking saveBookging(BookingDto reqBooking){ - User user = userService.getUser(reqBooking.getEmail()); - Performance performance = performanceService.getPerformance(reqBooking.getId(), reqBooking.getTitle()); - Seat seat = seatService.getSeat(performance, reqBooking.getSeatType(), reqBooking.getSeatNumber()); - - reqBooking.setUser(user); - reqBooking.setPerformance(performance); - reqBooking.setSeat(seat); + // @TODO : 동시성 문제 해결해야 함 + //예약 정보를 booking에 저장하고, Seat 정보에는 is_booking을 true로 변경 + @Transactional + public Booking saveBookging(final BookingDto reqBooking){ + final User user = userService.getUser(reqBooking.getEmail()); + final Performance performance = performanceService.getPerformance(reqBooking.getId(), reqBooking.getTitle()); + final Seat seat = seatService.getIsBooking(user, performance, reqBooking.getSeatType(), reqBooking.getSeatNumber(), false); //false라면 예약 가능 //seat 테이블의 is_booking 칼럼을 true로 update - SeatDto seatDto = new SeatDto(seat.getId(), performance, reqBooking.getSeatType(), reqBooking.getSeatNumber(), true); - seatService.updateSeat(seatDto); + updateSeat(seat, performance, reqBooking); - Booking booking = reqBooking.toEntity(); + //seat의 값이 있다면, booking 가능 + addBookingHistory(user, performance, seat); + + //booking 여부 insert + Booking booking = reqBooking.toEntity(user, performance, seat); return bookingRepository.save(booking); } - public Optional getMyBooking(String email){ + private void updateSeat(final Seat seat, final Performance performance, final BookingDto reqBooking) { + SeatDto seatDto = SeatDto.builder() + .seat_id(seat.getId()) + .performance(performance) + .seatType(reqBooking.getSeatType()) + .seatNumber(reqBooking.getSeatNumber()) + .isBooking(true) + .build(); + seatService.updateSeat(seatDto); + } + + private void addBookingHistory(final User user, final Performance performance, final Seat seat) { + //booking의 성공 여부 history 저장 + BookingHistoryDto bookingHistoryDto = BookingHistoryDto.builder() + .user(user) + .performance(performance) + .seat(seat) + .isSuccess(true) + .build(); + bookingHistoryService.saveBookingHistory(bookingHistoryDto); + } - return null; + @Transactional(readOnly = true) + public List getMyBooking(String email){ + User user = userService.getUser(email); + List result = bookingRepository.findMyBooking(true, user.getEmail()); + if(result.isEmpty()){ + throw new NotFoundDataException(); + } + return result; } } diff --git a/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java b/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java index 431f868..5282968 100644 --- a/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java +++ b/ticket/src/main/java/comento/backend/ticket/service/PerformanceService.java @@ -20,7 +20,7 @@ public PerformanceService(PerformanceRepository performanceRepository) { this.performanceRepository = performanceRepository; } - public List getListPerformance(PerformanceDto performanceDto){ + public List getListPerformance(final PerformanceDto performanceDto){ Date startDate = performanceDto.getStartDate(); String title = performanceDto.getTitle(); List result = title != null ? @@ -32,7 +32,7 @@ public List getListPerformance(PerformanceDto performanceDt return result.stream().map(PerformanceResponse::of).collect(Collectors.toList()); } - public Performance getPerformance(Long id, String title){ + public Performance getPerformance(final Long id, final String title){ Optional performance = performanceRepository.findByIdAndTitle(id, title); return performance.orElseThrow(NotFoundDataException::new); } diff --git a/ticket/src/main/java/comento/backend/ticket/service/SeatService.java b/ticket/src/main/java/comento/backend/ticket/service/SeatService.java index 22a0756..c4ba09c 100644 --- a/ticket/src/main/java/comento/backend/ticket/service/SeatService.java +++ b/ticket/src/main/java/comento/backend/ticket/service/SeatService.java @@ -1,14 +1,12 @@ package comento.backend.ticket.service; +import comento.backend.ticket.config.customException.DuplicatedException; import comento.backend.ticket.config.customException.NotFoundDataException; import comento.backend.ticket.domain.Performance; import comento.backend.ticket.domain.Seat; -import comento.backend.ticket.dto.PerformanceResponse; -import comento.backend.ticket.dto.SeatDto; -import comento.backend.ticket.dto.SeatResponse; -import comento.backend.ticket.dto.SeatType; +import comento.backend.ticket.domain.User; +import comento.backend.ticket.dto.*; import comento.backend.ticket.repository.SeatRepository; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; @@ -18,12 +16,14 @@ @Service public class SeatService { private final SeatRepository seatRepository; + private final BookingHistoryService bookingHistoryService; - public SeatService(SeatRepository seatRepository) { + public SeatService(SeatRepository seatRepository, BookingHistoryService bookingHistoryService) { this.seatRepository = seatRepository; + this.bookingHistoryService = bookingHistoryService; } - public List getListPerformanceSeat(PerformanceResponse performanceData) { + public List getListPerformanceSeat(final PerformanceResponse performanceData) { Performance performance = new Performance(); performance.setId(performanceData.getId()); List seatResult = seatRepository.findByPerformance(performance); @@ -33,13 +33,32 @@ public List getListPerformanceSeat(PerformanceResponse performance return seatResult.stream().map(SeatResponse::of).collect(Collectors.toList()); } - public Seat getSeat(Performance performance, SeatType seatType, Integer seatNumber) { - Optional seat = seatRepository.findByPerformanceAndSeatTypeAndSeatNumber(performance, seatType, seatNumber); - return seat.orElseThrow(NotFoundDataException::new); - } - - public Seat updateSeat(SeatDto seatDto) { + public Seat updateSeat(final SeatDto seatDto) { Seat seat = seatDto.toEntity(); return seatRepository.save(seat); } + + // @TODO : insert 쿼리도 출력되고, 예외도 발생하지만 BookingHisotry 테이블에 새 데이터 insert가 안된다 .. + public Seat getIsBooking(final User user, final Performance performance, final SeatType seatType, final Integer seatNumber, final boolean isBooking) { + Optional seatIsBooking = seatRepository.findByPerformanceAndSeatTypeAndSeatNumberAndIsBooking(performance, seatType, seatNumber, isBooking); + final Seat seat = getSeat(performance, seatType, seatNumber); //seat 번호를 위해서 필요 + return seatIsBooking.orElseGet(() -> { + //실패 여부 BookingHistory에 저장 + BookingHistoryDto bookingHistoryDto = BookingHistoryDto.builder() + .id(null) + .user(user) + .performance(performance) + .seat(seat) + .isSuccess(false) + .build(); + bookingHistoryService.saveBookingHistory(bookingHistoryDto); + //예외 전달 + throw new DuplicatedException("SeatService"); + }); + } + + public Seat getSeat(Performance performance, SeatType seatType, Integer seatNumber) { + Optional seat = seatRepository.findByPerformanceAndSeatTypeAndSeatNumber(performance, seatType, seatNumber); + return seat.orElse(seat.get()); + } } diff --git a/ticket/src/main/java/comento/backend/ticket/service/UserService.java b/ticket/src/main/java/comento/backend/ticket/service/UserService.java index 1cb1112..2890a98 100644 --- a/ticket/src/main/java/comento/backend/ticket/service/UserService.java +++ b/ticket/src/main/java/comento/backend/ticket/service/UserService.java @@ -19,7 +19,7 @@ public UserService(UserRepository userRepository) { this.userRepository = userRepository; } - public User saveUser(UserDto userDto){ + public User saveUser(final UserDto userDto){ User user = userDto.toEntity(); if(!checkEmailDuplicate(user.getEmail())) { @@ -29,12 +29,12 @@ public User saveUser(UserDto userDto){ return userRepository.save(user); } - private boolean checkEmailDuplicate(String email){ + private boolean checkEmailDuplicate(final String email){ User user = userRepository.findByEmail(email).orElseGet(()->null); return Objects.isNull(user); //null이면 true } - public User getUser(String email){ + public User getUser(final String email){ Optional user = userRepository.findByEmail(email); return user.orElseThrow(NoAuthException::new); } diff --git a/ticket/src/test/java/comento/backend/ticket/service/MockPerformanceServiceTest.java b/ticket/src/test/java/comento/backend/ticket/service/MockPerformanceServiceTest.java index 60fee9f..1e69149 100644 --- a/ticket/src/test/java/comento/backend/ticket/service/MockPerformanceServiceTest.java +++ b/ticket/src/test/java/comento/backend/ticket/service/MockPerformanceServiceTest.java @@ -49,7 +49,7 @@ void init() { //performanceRepository를 mock했으니 테스트 상황에서 원하는 결과를 새롭게 구현하는 코드. //테스트가 돌아가는 동안에는 willReturn에서 반환하는 결과를 반드시 return하므로 PerformanceRepository를 의존하지 않고도 돌아가는 테스트코드가 완성 given(performanceRepository.findByStartDateGreaterThanEqualOrderByStartDateAsc(startDate)) - .willReturn(new ArrayList()); + .willReturn(new ArrayList()); //빈 배열 반환 //when //공연정보가 없는 경우 Service에서는 NOT FOUND ERROR 예외 호출 NotFoundDataException nfde = assertThrows(NotFoundDataException.class, () -> { @@ -67,13 +67,12 @@ void init() { Date startDate = format.parse("2021-06-20"); given(performanceRepository.findByStartDateGreaterThanEqualOrderByStartDateAsc(startDate)) - .willReturn(new ArrayList<>()); + .willReturn(PerformanceFixture.performances(startDate)); //when List result = performanceService.getListPerformance(new PerformanceDto(null, startDate)); //then - System.out.println(result); - assertThat(result.size()).isNotZero(); //2개 + assertThat(result.size()).isNotZero(); } @Test @DisplayName("[성공] 날짜, 제목을 정확히 입력한 경우") @@ -84,12 +83,47 @@ void init() { String title = "국립무용단 <산조>"; PerformanceDto dto = new PerformanceDto(title, startDate); given(performanceRepository.findByTitleAndStartDateGreaterThanEqualOrderByStartDate(title, startDate)) - .willReturn(new ArrayList<>()); + .willReturn(PerformanceFixture.performances(startDate, title)); //when List result = performanceService.getListPerformance(dto); //then - System.out.println(result); assertThat(result.size()).isNotZero(); } + private static class PerformanceFixture { + public static List performances(final Date date) { + List performances = new ArrayList<>(); + performances.add(performance(date)); + return performances; + } + public static List performances(final Date date, final String title) { + List performances = new ArrayList<>(); + performances.add(performance(date, title)); + return performances; + } + public static Performance performance(final Date startDate) { + return Performance.builder() + .id(null) + .title(null) + .startDate(startDate) + .endDate(null) + .genere(null) + .description(null) + .price(null) + .runningTime(null) + .build(); + } + public static Performance performance(final Date startDate, final String title) { + return Performance.builder() + .id(null) + .title(title) + .startDate(startDate) + .endDate(null) + .genere(null) + .description(null) + .price(null) + .runningTime(null) + .build(); + } + } } \ No newline at end of file diff --git a/ticket/src/test/java/comento/backend/ticket/service/MockSeatServiceTest.java b/ticket/src/test/java/comento/backend/ticket/service/MockSeatServiceTest.java index 4e6d62a..c8659ac 100644 --- a/ticket/src/test/java/comento/backend/ticket/service/MockSeatServiceTest.java +++ b/ticket/src/test/java/comento/backend/ticket/service/MockSeatServiceTest.java @@ -3,9 +3,9 @@ import comento.backend.ticket.config.customException.NotFoundDataException; import comento.backend.ticket.domain.Performance; import comento.backend.ticket.domain.Seat; -import comento.backend.ticket.dto.PerformanceDto; import comento.backend.ticket.dto.PerformanceResponse; import comento.backend.ticket.dto.SeatResponse; +import comento.backend.ticket.dto.SeatType; import comento.backend.ticket.repository.SeatRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -32,22 +32,25 @@ public class MockSeatServiceTest { private SeatRepository seatRepository; private SeatService seatService; + private BookingHistoryService bookingHistoryService; @BeforeEach void init() { - seatService = new SeatService(seatRepository); //mock 주입 + seatService = new SeatService(seatRepository, bookingHistoryService); //mock 주입 } @Test @DisplayName("[실패] 404 NOT FOUND ERROR") void 좌석_정보가_없으면_예외를_던진다() { //given - Long id = 1L; + Performance performance = new Performance(1L, null, null, null, null, null, null, null); + PerformanceResponse performanceResponse = new PerformanceResponse(performance.getId(), "2022 정오의 음악회 5월", null, null, + "전석 20,000원", null, null, null); - given(seatRepository.findByPerformanceId(id)) - .willReturn(new ArrayList()); + given(seatRepository.findByPerformance(performance)) + .willReturn(new ArrayList<>()); //when NotFoundDataException nfde = assertThrows(NotFoundDataException.class, () -> { - seatService.getListPerformanceSeat(id); + seatService.getListPerformanceSeat(performanceResponse); }); //then String msg = nfde.getMessage(); @@ -64,13 +67,29 @@ void init() { Performance performance = new Performance(); performance.setId(performanceResponse.getId()); - given(seatRepository.findByPerformance(performance)) - .willReturn(new ArrayList()); + .willReturn(SeatFixture.seats()); //when List list = seatService.getListPerformanceSeat(performanceResponse); //then assertThat(list.size()).isNotZero(); } + + //반복적으로 테스트하여 사용할 객체를 위한 클래스 생성 + private static class SeatFixture { + public static List seats() { + List seats = new ArrayList<>(); + seats.add(seat()); + return seats; + } + public static Seat seat() { + return Seat.builder() + .id(1L) + .seatType(SeatType.S) + .seatNumber(1) + .performance(new Performance()) + .build(); + } + } } From 5085387d52f5d9287e58846dc2f09043273ca6b3 Mon Sep 17 00:00:00 2001 From: hyejungg Date: Sun, 3 Oct 2021 00:22:16 +0900 Subject: [PATCH 8/8] =?UTF-8?q?[fix:#11]=20=ED=94=BC=EB=93=9C=EB=B0=B1=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 피드백을 반영하여 getIsBooking()메소드에서 seat 정보를 2번 read 하는 작업을 -> 1개로 수정하였습니다. - history에 저장되는 메소드 이름을 saveBookingXXXlog(fail,success)통일 시켰습니다. - insert, update 등이 수행되는 부분에는 @Transcational을 추가하고, 조회하는 경우에는 readOnly를 추가하였습니다. --- .../ticket/service/BookingService.java | 4 +- .../backend/ticket/service/SeatService.java | 37 ++++++++++--------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/ticket/src/main/java/comento/backend/ticket/service/BookingService.java b/ticket/src/main/java/comento/backend/ticket/service/BookingService.java index 56b671e..4e8702c 100644 --- a/ticket/src/main/java/comento/backend/ticket/service/BookingService.java +++ b/ticket/src/main/java/comento/backend/ticket/service/BookingService.java @@ -40,7 +40,7 @@ public Booking saveBookging(final BookingDto reqBooking){ updateSeat(seat, performance, reqBooking); //seat의 값이 있다면, booking 가능 - addBookingHistory(user, performance, seat); + saveBookingSucessLog(user, performance, seat); //booking 여부 insert Booking booking = reqBooking.toEntity(user, performance, seat); @@ -58,7 +58,7 @@ private void updateSeat(final Seat seat, final Performance performance, final Bo seatService.updateSeat(seatDto); } - private void addBookingHistory(final User user, final Performance performance, final Seat seat) { + private void saveBookingSucessLog(final User user, final Performance performance, final Seat seat) { //booking의 성공 여부 history 저장 BookingHistoryDto bookingHistoryDto = BookingHistoryDto.builder() .user(user) diff --git a/ticket/src/main/java/comento/backend/ticket/service/SeatService.java b/ticket/src/main/java/comento/backend/ticket/service/SeatService.java index c4ba09c..c50d32e 100644 --- a/ticket/src/main/java/comento/backend/ticket/service/SeatService.java +++ b/ticket/src/main/java/comento/backend/ticket/service/SeatService.java @@ -8,8 +8,10 @@ import comento.backend.ticket.dto.*; import comento.backend.ticket.repository.SeatRepository; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -23,6 +25,7 @@ public SeatService(SeatRepository seatRepository, BookingHistoryService bookingH this.bookingHistoryService = bookingHistoryService; } + @Transactional(readOnly = true) public List getListPerformanceSeat(final PerformanceResponse performanceData) { Performance performance = new Performance(); performance.setId(performanceData.getId()); @@ -39,26 +42,26 @@ public Seat updateSeat(final SeatDto seatDto) { } // @TODO : insert 쿼리도 출력되고, 예외도 발생하지만 BookingHisotry 테이블에 새 데이터 insert가 안된다 .. + @Transactional public Seat getIsBooking(final User user, final Performance performance, final SeatType seatType, final Integer seatNumber, final boolean isBooking) { - Optional seatIsBooking = seatRepository.findByPerformanceAndSeatTypeAndSeatNumberAndIsBooking(performance, seatType, seatNumber, isBooking); - final Seat seat = getSeat(performance, seatType, seatNumber); //seat 번호를 위해서 필요 - return seatIsBooking.orElseGet(() -> { - //실패 여부 BookingHistory에 저장 - BookingHistoryDto bookingHistoryDto = BookingHistoryDto.builder() - .id(null) - .user(user) - .performance(performance) - .seat(seat) - .isSuccess(false) - .build(); - bookingHistoryService.saveBookingHistory(bookingHistoryDto); - //예외 전달 + Seat seatIsBooking = seatRepository.findByPerformanceAndSeatTypeAndSeatNumberAndIsBooking(performance, seatType, seatNumber, isBooking) + .orElseGet(()-> null); + if(Objects.isNull(seatIsBooking)){ + saveBookingFailLog(user, performance, null); throw new DuplicatedException("SeatService"); - }); + } + return seatIsBooking; } - public Seat getSeat(Performance performance, SeatType seatType, Integer seatNumber) { - Optional seat = seatRepository.findByPerformanceAndSeatTypeAndSeatNumber(performance, seatType, seatNumber); - return seat.orElse(seat.get()); + private void saveBookingFailLog(final User user, final Performance performance, final Seat seat) { + //실패 여부 BookingHistory에 저장 + BookingHistoryDto bookingHistoryDto = BookingHistoryDto.builder() + .id(null) + .user(user) + .performance(performance) + .seat(seat) + .isSuccess(false) + .build(); + bookingHistoryService.saveBookingHistory(bookingHistoryDto); } }