diff --git a/server/src/main/java/com/ticketing/server/movie/application/TicketController.java b/server/src/main/java/com/ticketing/server/movie/application/TicketController.java index d55ffac..5091418 100644 --- a/server/src/main/java/com/ticketing/server/movie/application/TicketController.java +++ b/server/src/main/java/com/ticketing/server/movie/application/TicketController.java @@ -1,8 +1,30 @@ package com.ticketing.server.movie.application; +import com.ticketing.server.movie.application.response.TicketDetailsResponse; +import com.ticketing.server.movie.service.dto.TicketDetailsDTO; +import com.ticketing.server.movie.service.interfaces.TicketService; +import javax.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController +@RequiredArgsConstructor +@RequestMapping("/api/tickets") +@Validated public class TicketController { + private final TicketService ticketService; + + @GetMapping("payments/{paymentId}") + public ResponseEntity findTicketsByPaymentId(@PathVariable("paymentId") @NotNull Long paymentId) { + TicketDetailsDTO tickets = ticketService.findTicketsByPaymentId(paymentId); + return ResponseEntity.status(HttpStatus.OK).body(tickets.toResponse()); + } + } diff --git a/server/src/main/java/com/ticketing/server/movie/application/response/TicketDetailsResponse.java b/server/src/main/java/com/ticketing/server/movie/application/response/TicketDetailsResponse.java new file mode 100644 index 0000000..9f316b3 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/movie/application/response/TicketDetailsResponse.java @@ -0,0 +1,16 @@ +package com.ticketing.server.movie.application.response; + +import com.ticketing.server.payment.application.response.TicketDetailDTO; +import java.util.List; +import lombok.Getter; + +@Getter +public class TicketDetailsResponse { + + private final List ticketDetails; + + public TicketDetailsResponse(List ticketDetails) { + this.ticketDetails = ticketDetails; + } + +} diff --git a/server/src/main/java/com/ticketing/server/movie/domain/MovieTime.java b/server/src/main/java/com/ticketing/server/movie/domain/MovieTime.java index 866a801..2d21acc 100644 --- a/server/src/main/java/com/ticketing/server/movie/domain/MovieTime.java +++ b/server/src/main/java/com/ticketing/server/movie/domain/MovieTime.java @@ -6,25 +6,27 @@ import java.util.ArrayList; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.validation.constraints.NotNull; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @Entity @Getter -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class MovieTime extends AbstractEntity { @NotNull - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "movie_id", referencedColumnName = "id", updatable = false) private Movie movie; @NotNull - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "theater_id", referencedColumnName = "id", updatable = false) private Theater theater; @@ -40,19 +42,30 @@ public class MovieTime extends AbstractEntity { @OneToMany(mappedBy = "movieTime", cascade = CascadeType.ALL) private List tickets = new ArrayList<>(); - private MovieTime(Movie movie, Theater theater, int round, LocalDateTime startAt, LocalDateTime endAt) { + public MovieTime(Movie movie, Theater theater, int round, LocalDateTime startAt) { this.movie = movie; this.theater = theater; this.round = round; this.startAt = startAt; - this.endAt = endAt; + this.endAt = generateEndAt(startAt); } - public static MovieTime of(Movie movie, Theater theater, int round, LocalDateTime startAt) { - Long runningTime = movie.getRunningTime(); - LocalDateTime endAt = startAt.plusMinutes(runningTime); + MovieTime(Long id, Movie movie, Theater theater, int round, LocalDateTime startAt) { + this.id = id; + this.movie = movie; + this.theater = theater; + this.round = round; + this.startAt = startAt; + this.endAt = generateEndAt(startAt); + } - return new MovieTime(movie, theater, round, startAt, endAt); + private LocalDateTime generateEndAt(LocalDateTime startAt) { + Long runningTime = movie.getRunningTime(); + return startAt.plusMinutes(runningTime); + } + + public String getMovieTitle() { + return this.movie.getTitle(); } public List getSeats() { diff --git a/server/src/main/java/com/ticketing/server/movie/domain/Seat.java b/server/src/main/java/com/ticketing/server/movie/domain/Seat.java index 89190d2..d6b3271 100644 --- a/server/src/main/java/com/ticketing/server/movie/domain/Seat.java +++ b/server/src/main/java/com/ticketing/server/movie/domain/Seat.java @@ -2,31 +2,40 @@ package com.ticketing.server.movie.domain; import com.ticketing.server.global.dto.repository.AbstractEntity; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.validation.constraints.NotNull; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @Entity @Getter -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Seat extends AbstractEntity { @NotNull - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "theater_id", referencedColumnName = "id", updatable = false) private Theater theater; - @NotNull - private Integer seatRow; - @NotNull private Integer seatColumn; - public Seat(Integer seatRow, Integer seatColumn, Theater theater) { - this.seatRow = seatRow; + @NotNull + private Integer seatRow; + + public Seat(Integer seatColumn, Integer seatRow, Theater theater) { this.seatColumn = seatColumn; + this.seatRow = seatRow; + setTheater(theater); + } + + Seat(Long id, int column, int row, Theater theater) { + this.id = id; + this.seatColumn = column; + this.seatRow = row; setTheater(theater); } @@ -34,4 +43,9 @@ public class Seat extends AbstractEntity { this.theater = theater; theater.addSeat(this); } + + public Integer getTheaterNumber() { + return this.theater.getTheaterNumber(); + } + } diff --git a/server/src/main/java/com/ticketing/server/movie/domain/Theater.java b/server/src/main/java/com/ticketing/server/movie/domain/Theater.java index 1baf364..4b75ed1 100644 --- a/server/src/main/java/com/ticketing/server/movie/domain/Theater.java +++ b/server/src/main/java/com/ticketing/server/movie/domain/Theater.java @@ -7,12 +7,13 @@ import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.OneToMany; import javax.validation.constraints.NotNull; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @Entity @Getter -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Theater extends AbstractEntity { @NotNull @@ -21,6 +22,11 @@ public class Theater extends AbstractEntity { @OneToMany(mappedBy = "theater", cascade = CascadeType.ALL) private List seats = new ArrayList<>(); + Theater(Long id, Integer theaterNumber) { + this.id = id; + this.theaterNumber = theaterNumber; + } + public Theater(Integer theaterNumber) { this.theaterNumber = theaterNumber; } diff --git a/server/src/main/java/com/ticketing/server/movie/domain/Ticket.java b/server/src/main/java/com/ticketing/server/movie/domain/Ticket.java index 7507ebf..94c72e6 100644 --- a/server/src/main/java/com/ticketing/server/movie/domain/Ticket.java +++ b/server/src/main/java/com/ticketing/server/movie/domain/Ticket.java @@ -1,27 +1,31 @@ package com.ticketing.server.movie.domain; import com.ticketing.server.global.dto.repository.AbstractEntity; +import com.ticketing.server.global.exception.ErrorCode; +import java.time.LocalDateTime; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; +import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.validation.constraints.NotNull; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @Entity @Getter -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Ticket extends AbstractEntity { @NotNull - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "seat_id", referencedColumnName = "id", updatable = false) private Seat seat; @NotNull - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "movie_times_id", referencedColumnName = "id", updatable = false) private MovieTime movieTime; @@ -34,15 +38,52 @@ public class Ticket extends AbstractEntity { @NotNull private Integer ticketPrice; - private Ticket(Seat seat, MovieTime movieTime, Integer ticketPrice) { + public Ticket(Seat seat, MovieTime movieTime, Integer ticketPrice) { this.seat = seat; this.movieTime = movieTime; this.ticketPrice = ticketPrice; this.status = TicketStatus.SALE; } - public static Ticket of(Seat seat, MovieTime movieTime, Integer ticketPrice) { - return new Ticket(seat, movieTime, ticketPrice); + Ticket(Long id, Seat seat, MovieTime movieTime, Integer ticketPrice) { + this.id = id; + this.seat = seat; + this.movieTime = movieTime; + this.ticketPrice = ticketPrice; + this.status = TicketStatus.SALE; } + public Integer getColumn() { + return this.seat.getSeatColumn(); + } + + public Integer getRow() { + return this.seat.getSeatRow(); + } + + public LocalDateTime getStartAt() { + return this.movieTime.getStartAt(); + } + + public LocalDateTime getEndAt() { + return this.movieTime.getEndAt(); + } + + public Integer getTheaterNumber() { + return this.seat.getTheaterNumber(); + } + + public String getMovieTitle() { + return this.movieTime.getMovieTitle(); + } + + public void registerPayment(Long id) { + if (this.paymentId != null) { + throw ErrorCode.throwDuplicatePayment(); + } + + this.paymentId = id; + } + + } diff --git a/server/src/main/java/com/ticketing/server/movie/domain/repository/TicketRepository.java b/server/src/main/java/com/ticketing/server/movie/domain/repository/TicketRepository.java index 362a19e..4298b6f 100644 --- a/server/src/main/java/com/ticketing/server/movie/domain/repository/TicketRepository.java +++ b/server/src/main/java/com/ticketing/server/movie/domain/repository/TicketRepository.java @@ -1,10 +1,24 @@ package com.ticketing.server.movie.domain.repository; import com.ticketing.server.movie.domain.Ticket; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository public interface TicketRepository extends JpaRepository { + @Query( + value = + "SELECT t " + + "FROM Ticket t " + + "JOIN FETCH t.movieTime mt " + + "JOIN FETCH t.seat s " + + "JOIN FETCH s.theater th " + + "WHERE t.paymentId = :paymentId " + ) + List findTicketFetchJoinByPaymentId(@Param("paymentId") Long paymentId); + } diff --git a/server/src/main/java/com/ticketing/server/movie/service/TicketServiceImpl.java b/server/src/main/java/com/ticketing/server/movie/service/TicketServiceImpl.java index c4c9eb0..10b72fe 100644 --- a/server/src/main/java/com/ticketing/server/movie/service/TicketServiceImpl.java +++ b/server/src/main/java/com/ticketing/server/movie/service/TicketServiceImpl.java @@ -1,9 +1,40 @@ package com.ticketing.server.movie.service; +import com.ticketing.server.global.exception.ErrorCode; +import com.ticketing.server.movie.domain.repository.TicketRepository; +import com.ticketing.server.movie.service.dto.TicketDetailsDTO; import com.ticketing.server.movie.service.interfaces.TicketService; +import com.ticketing.server.payment.application.response.TicketDetailDTO; +import java.util.List; +import java.util.stream.Collectors; +import javax.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; @Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +@Validated +@Slf4j public class TicketServiceImpl implements TicketService { + private final TicketRepository ticketRepository; + + @Override + public TicketDetailsDTO findTicketsByPaymentId(@NotNull Long paymentId) { + List ticketDetails = ticketRepository.findTicketFetchJoinByPaymentId(paymentId) + .stream() + .map(TicketDetailDTO::new) + .collect(Collectors.toList()); + + if (ticketDetails.isEmpty()) { + throw ErrorCode.throwPaymentIdNotFound(); + } + + return new TicketDetailsDTO(ticketDetails); + } + } diff --git a/server/src/main/java/com/ticketing/server/movie/service/dto/TicketDetailsDTO.java b/server/src/main/java/com/ticketing/server/movie/service/dto/TicketDetailsDTO.java new file mode 100644 index 0000000..3a36d2b --- /dev/null +++ b/server/src/main/java/com/ticketing/server/movie/service/dto/TicketDetailsDTO.java @@ -0,0 +1,18 @@ +package com.ticketing.server.movie.service.dto; + +import com.ticketing.server.movie.application.response.TicketDetailsResponse; +import com.ticketing.server.payment.application.response.TicketDetailDTO; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class TicketDetailsDTO { + + private final List ticketDetails; + + public TicketDetailsResponse toResponse() { + return new TicketDetailsResponse(ticketDetails); + } +} diff --git a/server/src/main/java/com/ticketing/server/movie/service/interfaces/TicketService.java b/server/src/main/java/com/ticketing/server/movie/service/interfaces/TicketService.java index a994d32..46a3151 100644 --- a/server/src/main/java/com/ticketing/server/movie/service/interfaces/TicketService.java +++ b/server/src/main/java/com/ticketing/server/movie/service/interfaces/TicketService.java @@ -1,5 +1,10 @@ package com.ticketing.server.movie.service.interfaces; +import com.ticketing.server.movie.service.dto.TicketDetailsDTO; +import javax.validation.constraints.NotNull; + public interface TicketService { + TicketDetailsDTO findTicketsByPaymentId(@NotNull Long paymentId); + } diff --git a/server/src/main/java/com/ticketing/server/movie/setup/MovieSetupService.java b/server/src/main/java/com/ticketing/server/movie/setup/MovieSetupService.java index aab9e62..8fde7b4 100644 --- a/server/src/main/java/com/ticketing/server/movie/setup/MovieSetupService.java +++ b/server/src/main/java/com/ticketing/server/movie/setup/MovieSetupService.java @@ -9,11 +9,20 @@ import com.ticketing.server.movie.domain.repository.MovieRepository; import com.ticketing.server.movie.domain.repository.MovieTimeRepository; import com.ticketing.server.movie.domain.repository.TheaterRepository; import com.ticketing.server.movie.domain.repository.TicketRepository; +import com.ticketing.server.payment.domain.Payment; +import com.ticketing.server.payment.domain.PaymentStatus; +import com.ticketing.server.payment.domain.PaymentType; +import com.ticketing.server.payment.domain.repository.PaymentRepository; +import com.ticketing.server.payment.service.dto.CreatePaymentDTO; +import com.ticketing.server.user.domain.User; +import com.ticketing.server.user.domain.UserGrade; +import com.ticketing.server.user.domain.repository.UserRepository; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,10 +31,14 @@ import org.springframework.transaction.annotation.Transactional; public class MovieSetupService { + private final UserRepository userRepository; private final MovieRepository movieRepository; private final MovieTimeRepository movieTimeRepository; private final TheaterRepository theaterRepository; private final TicketRepository ticketRepository; + private final PaymentRepository paymentRepository; + + private final PasswordEncoder passwordEncoder; @Transactional public void init() { @@ -33,6 +46,7 @@ MovieSetupService { initTheater(); initMovieTime(); initTicket(); + initPayment(); } private void initMovie() { @@ -54,9 +68,9 @@ MovieSetupService { ); for (Theater theater : theaters) { - for (int row = 1; row <= 2; row++) { - for (int col = 1; col <= 10; col++) { - new Seat(row, col, theater); + for (int col = 1; col <= 2; col++) { + for (int row = 1; row <= 10; row++) { + new Seat(col, row, theater); } } } @@ -72,14 +86,14 @@ MovieSetupService { List movieTimes = new ArrayList<>(); for (Theater theater : theaters) { - movieTimes.add(MovieTime.of(movies.get(0), theater, 1, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 8, 0))); - movieTimes.add(MovieTime.of(movies.get(0), theater, 3, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 12, 0))); - movieTimes.add(MovieTime.of(movies.get(1), theater, 2, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 10, 0))); - movieTimes.add(MovieTime.of(movies.get(2), theater, 4, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 14, 0))); - movieTimes.add(MovieTime.of(movies.get(0), theater, 5, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 16, 0))); - movieTimes.add(MovieTime.of(movies.get(3), theater, 6, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 18, 0))); - movieTimes.add(MovieTime.of(movies.get(0), theater, 7, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 21, 0))); - movieTimes.add(MovieTime.of(movies.get(4), theater, 8, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 23, 0))); + movieTimes.add(new MovieTime(movies.get(0), theater, 1, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 8, 0))); + movieTimes.add(new MovieTime(movies.get(0), theater, 3, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 12, 0))); + movieTimes.add(new MovieTime(movies.get(1), theater, 2, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 10, 0))); + movieTimes.add(new MovieTime(movies.get(2), theater, 4, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 14, 0))); + movieTimes.add(new MovieTime(movies.get(0), theater, 5, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 16, 0))); + movieTimes.add(new MovieTime(movies.get(3), theater, 6, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 18, 0))); + movieTimes.add(new MovieTime(movies.get(0), theater, 7, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 21, 0))); + movieTimes.add(new MovieTime(movies.get(4), theater, 8, LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 23, 0))); } movieTimeRepository.saveAll(movieTimes); @@ -92,11 +106,26 @@ MovieSetupService { Integer ticketPrice = 15_000; for (MovieTime movieTime : movieTimes) { for (Seat seat : movieTime.getSeats()) { - tickets.add(Ticket.of(seat, movieTime, ticketPrice)); + tickets.add(new Ticket(seat, movieTime, ticketPrice)); } } ticketRepository.saveAll(tickets); } + private void initPayment() { + User user = userRepository.save(new User(123L, "김동효", "kdhyo98@gmail.com", passwordEncoder.encode("123123"), UserGrade.USER, "010-1234-5678")); + + List tickets = ticketRepository.findAll(); + Ticket ticket = tickets.get(0); + String title = ticket.getMovieTime().getMovie().getTitle(); + CreatePaymentDTO createPaymentDto = new CreatePaymentDTO(user.getAlternateId(), title, PaymentType.KAKAO_PAY, PaymentStatus.COMPLETED, "2022-0710-4142", 30_000); + Payment payment = createPaymentDto.toEntity(); + + paymentRepository.save(payment); + + ticket.registerPayment(payment.getId()); + tickets.get(1).registerPayment(payment.getId()); + } + } diff --git a/server/src/main/java/com/ticketing/server/payment/api/MovieClient.java b/server/src/main/java/com/ticketing/server/payment/api/MovieClient.java new file mode 100644 index 0000000..a76643b --- /dev/null +++ b/server/src/main/java/com/ticketing/server/payment/api/MovieClient.java @@ -0,0 +1,10 @@ +package com.ticketing.server.payment.api; + +import com.ticketing.server.movie.application.response.TicketDetailsResponse; +import javax.validation.constraints.NotNull; + +public interface MovieClient { + + TicketDetailsResponse getTicketsByPaymentId(@NotNull Long paymentId); + +} diff --git a/server/src/main/java/com/ticketing/server/payment/api/UserClient.java b/server/src/main/java/com/ticketing/server/payment/api/UserClient.java new file mode 100644 index 0000000..da7ddae --- /dev/null +++ b/server/src/main/java/com/ticketing/server/payment/api/UserClient.java @@ -0,0 +1,8 @@ +package com.ticketing.server.payment.api; + +import com.ticketing.server.payment.api.dto.response.UserDetailResponse; + +public interface UserClient { + + UserDetailResponse detail(); +} diff --git a/server/src/main/java/com/ticketing/server/payment/api/dto/response/UserDetailResponse.java b/server/src/main/java/com/ticketing/server/payment/api/dto/response/UserDetailResponse.java new file mode 100644 index 0000000..1136150 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/payment/api/dto/response/UserDetailResponse.java @@ -0,0 +1,34 @@ +package com.ticketing.server.payment.api.dto.response; + +import com.ticketing.server.payment.domain.Payment; +import com.ticketing.server.user.domain.UserGrade; +import com.ticketing.server.user.service.dto.UserDetailDTO; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class UserDetailResponse { + + private final Long userAlternateId; + private final String name; + private final String email; + private final UserGrade grade; + private final String phone; + + public UserDetailResponse(UserDetailDTO userDetailDto) { + this( + userDetailDto.getAlternateId(), + userDetailDto.getName(), + userDetailDto.getEmail(), + userDetailDto.getGrade(), + userDetailDto.getPhone() + ); + } + + public boolean hasUserAlternateId(Payment payment) { + return userAlternateId.equals(payment.getUserAlternateId()); + } + +} diff --git a/server/src/main/java/com/ticketing/server/payment/api/impl/MovieClientImpl.java b/server/src/main/java/com/ticketing/server/payment/api/impl/MovieClientImpl.java new file mode 100644 index 0000000..025ab72 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/payment/api/impl/MovieClientImpl.java @@ -0,0 +1,25 @@ +package com.ticketing.server.payment.api.impl; + +import com.ticketing.server.movie.application.response.TicketDetailsResponse; +import com.ticketing.server.movie.service.dto.TicketDetailsDTO; +import com.ticketing.server.movie.service.interfaces.TicketService; +import com.ticketing.server.payment.api.MovieClient; +import javax.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +@Service +@RequiredArgsConstructor +@Validated +public class MovieClientImpl implements MovieClient { + + private final TicketService ticketService; + + @Override + public TicketDetailsResponse getTicketsByPaymentId(@NotNull Long paymentId) { + TicketDetailsDTO ticketDetails = ticketService.findTicketsByPaymentId(paymentId); + return ticketDetails.toResponse(); + } + +} diff --git a/server/src/main/java/com/ticketing/server/payment/api/impl/UserClientImpl.java b/server/src/main/java/com/ticketing/server/payment/api/impl/UserClientImpl.java new file mode 100644 index 0000000..02e6c66 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/payment/api/impl/UserClientImpl.java @@ -0,0 +1,28 @@ +package com.ticketing.server.payment.api.impl; + +import com.ticketing.server.payment.api.UserClient; +import com.ticketing.server.payment.api.dto.response.UserDetailResponse; +import com.ticketing.server.user.service.dto.UserDetailDTO; +import com.ticketing.server.user.service.interfaces.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class UserClientImpl implements UserClient { + + private final UserService userService; + + @Override + public UserDetailResponse detail() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + UserDetails details = (UserDetails) authentication.getPrincipal(); + + UserDetailDTO userDetail = userService.findDetailByEmail(details.getUsername()); + return new UserDetailResponse(userDetail); + } + +} diff --git a/server/src/main/java/com/ticketing/server/payment/application/PaymentController.java b/server/src/main/java/com/ticketing/server/payment/application/PaymentController.java index 4751887..7510fbf 100644 --- a/server/src/main/java/com/ticketing/server/payment/application/PaymentController.java +++ b/server/src/main/java/com/ticketing/server/payment/application/PaymentController.java @@ -1,8 +1,42 @@ package com.ticketing.server.payment.application; +import com.ticketing.server.payment.application.response.PaymentDetailResponse; +import com.ticketing.server.payment.service.dto.SimplePaymentsDTO; +import com.ticketing.server.payment.service.interfaces.PaymentApisService; +import com.ticketing.server.payment.service.interfaces.PaymentService; +import com.ticketing.server.payment.application.response.SimplePaymentsResponse; +import com.ticketing.server.user.domain.UserGrade; +import javax.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController +@RequiredArgsConstructor +@RequestMapping("/api/payments") +@Validated public class PaymentController { + private final PaymentApisService paymentApisService; + private final PaymentService paymentService; + + @GetMapping + @Secured(UserGrade.ROLES.USER) + public ResponseEntity simplePayments(@NotNull Long userAlternateId) { + SimplePaymentsDTO simplePayments = paymentService.findSimplePayments(userAlternateId); + return ResponseEntity.status(HttpStatus.OK).body(simplePayments.toResponse()); + } + + @GetMapping("/detail") + @Secured(UserGrade.ROLES.USER) + public ResponseEntity detail(@NotNull Long paymentId) { + PaymentDetailResponse paymentDetail = paymentApisService.findPaymentDetail(paymentId); + return ResponseEntity.status(HttpStatus.OK).body(paymentDetail); + } + } diff --git a/server/src/main/java/com/ticketing/server/payment/application/response/PaymentDetailResponse.java b/server/src/main/java/com/ticketing/server/payment/application/response/PaymentDetailResponse.java new file mode 100644 index 0000000..ef92a8e --- /dev/null +++ b/server/src/main/java/com/ticketing/server/payment/application/response/PaymentDetailResponse.java @@ -0,0 +1,38 @@ +package com.ticketing.server.payment.application.response; + +import com.ticketing.server.movie.application.response.TicketDetailsResponse; +import com.ticketing.server.payment.domain.Payment; +import java.time.LocalDateTime; +import java.util.List; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class PaymentDetailResponse { + + private Long paymentId; + + private String movieTitle; + + private String paymentNumber; + + private Integer totalPrice; + + private LocalDateTime createdAt; + + List tickets; + + public PaymentDetailResponse(Payment payment, TicketDetailsResponse ticketDetails) { + this( + payment.getId(), + payment.getMovieTitle(), + payment.getPaymentNumber(), + payment.getTotalPrice(), + payment.getCreatedAt(), + ticketDetails.getTicketDetails() + ); + } + +} diff --git a/server/src/main/java/com/ticketing/server/payment/application/response/SimplePaymentsResponse.java b/server/src/main/java/com/ticketing/server/payment/application/response/SimplePaymentsResponse.java new file mode 100644 index 0000000..a58f261 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/payment/application/response/SimplePaymentsResponse.java @@ -0,0 +1,16 @@ +package com.ticketing.server.payment.application.response; + +import com.ticketing.server.payment.service.dto.SimplePaymentDTO; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class SimplePaymentsResponse { + + private final Long userAlternateId; + + private final List payments; + +} diff --git a/server/src/main/java/com/ticketing/server/payment/application/response/TicketDetailDTO.java b/server/src/main/java/com/ticketing/server/payment/application/response/TicketDetailDTO.java new file mode 100644 index 0000000..98bc8a7 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/payment/application/response/TicketDetailDTO.java @@ -0,0 +1,36 @@ +package com.ticketing.server.payment.application.response; + +import com.ticketing.server.movie.domain.Ticket; +import java.time.LocalDateTime; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class TicketDetailDTO { + + private Integer price; + + private Integer theaterNumber; + + private Integer column; + + private Integer row; + + private LocalDateTime startAt; + + private LocalDateTime endAt; + + public TicketDetailDTO(Ticket ticket) { + this( + ticket.getTicketPrice(), + ticket.getTheaterNumber(), + ticket.getColumn(), + ticket.getRow(), + ticket.getStartAt(), + ticket.getEndAt() + ); + } + +} diff --git a/server/src/main/java/com/ticketing/server/payment/domain/Payment.java b/server/src/main/java/com/ticketing/server/payment/domain/Payment.java index 4ffef40..8d5b7e1 100644 --- a/server/src/main/java/com/ticketing/server/payment/domain/Payment.java +++ b/server/src/main/java/com/ticketing/server/payment/domain/Payment.java @@ -1,7 +1,7 @@ package com.ticketing.server.payment.domain; import com.ticketing.server.global.dto.repository.AbstractEntity; -import com.ticketing.server.payment.service.dto.CreatePaymentDto; +import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; @@ -17,29 +17,36 @@ import lombok.NoArgsConstructor; public class Payment extends AbstractEntity { @NotNull - private Long userId; + @Column(name = "user_alternate_id", nullable = false) + private Long userAlternateId; @NotEmpty + @Column(name = "movie_title", nullable = false) private String movieTitle; @NotNull + @Column(name = "type", nullable = false) @Enumerated(value = EnumType.STRING) private PaymentType type; @NotNull + @Column(name = "status", nullable = false) @Enumerated(value = EnumType.STRING) private PaymentStatus status; + @Column(name = "failed_message") private String failedMessage; @NotEmpty + @Column(name = "payment_number", nullable = false) private String paymentNumber; @NotNull + @Column(name = "total_price", nullable = false) private Integer totalPrice; - private Payment(Long userId, String movieTitle, PaymentType type, PaymentStatus status, String paymentNumber, Integer totalPrice) { - this.userId = userId; + public Payment(Long userAlternateId, String movieTitle, PaymentType type, PaymentStatus status, String paymentNumber, Integer totalPrice) { + this.userAlternateId = userAlternateId; this.movieTitle = movieTitle; this.type = type; this.status = status; @@ -47,13 +54,14 @@ public class Payment extends AbstractEntity { this.totalPrice = totalPrice; } - public static Payment from(CreatePaymentDto dto) { - return new Payment(dto.getUserId(), - dto.getMovieTitle(), - dto.getType(), - dto.getStatus(), - dto.getPaymentNumber(), - dto.getTotalPrice()); + Payment(Long id, Long userAlternateId, String movieTitle, PaymentType type, PaymentStatus status, String paymentNumber, Integer totalPrice) { + this.id = id; + this.userAlternateId = userAlternateId; + this.movieTitle = movieTitle; + this.type = type; + this.status = status; + this.paymentNumber = paymentNumber; + this.totalPrice = totalPrice; } } diff --git a/server/src/main/java/com/ticketing/server/payment/domain/repository/PaymentRepository.java b/server/src/main/java/com/ticketing/server/payment/domain/repository/PaymentRepository.java index 0feab8d..0956365 100644 --- a/server/src/main/java/com/ticketing/server/payment/domain/repository/PaymentRepository.java +++ b/server/src/main/java/com/ticketing/server/payment/domain/repository/PaymentRepository.java @@ -8,5 +8,6 @@ import org.springframework.stereotype.Repository; @Repository public interface PaymentRepository extends JpaRepository { - List findByUserId(Long userId); + List findByUserAlternateId(Long userId); + } diff --git a/server/src/main/java/com/ticketing/server/payment/service/PaymentApisServiceImpl.java b/server/src/main/java/com/ticketing/server/payment/service/PaymentApisServiceImpl.java new file mode 100644 index 0000000..e987880 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/payment/service/PaymentApisServiceImpl.java @@ -0,0 +1,44 @@ +package com.ticketing.server.payment.service; + +import com.ticketing.server.global.exception.ErrorCode; +import com.ticketing.server.movie.application.response.TicketDetailsResponse; +import com.ticketing.server.payment.api.MovieClient; +import com.ticketing.server.payment.api.UserClient; +import com.ticketing.server.payment.api.dto.response.UserDetailResponse; +import com.ticketing.server.payment.application.response.PaymentDetailResponse; +import com.ticketing.server.payment.domain.Payment; +import com.ticketing.server.payment.domain.repository.PaymentRepository; +import com.ticketing.server.payment.service.interfaces.PaymentApisService; +import javax.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +@Validated +@Slf4j +public class PaymentApisServiceImpl implements PaymentApisService { + + private final PaymentRepository paymentRepository; + private final MovieClient movieClient; + private final UserClient userClient; + + @Override + public PaymentDetailResponse findPaymentDetail(@NotNull Long paymentId) { + Payment payment = paymentRepository.findById(paymentId) + .orElseThrow(ErrorCode::throwPaymentIdNotFound); + + UserDetailResponse userDetail = userClient.detail(); + if (!userDetail.hasUserAlternateId(payment)) { + throw ErrorCode.throwValidUserId(); + } + + TicketDetailsResponse tickets = movieClient.getTicketsByPaymentId(payment.getId()); + return new PaymentDetailResponse(payment, tickets); + } + +} diff --git a/server/src/main/java/com/ticketing/server/payment/service/PaymentServiceImpl.java b/server/src/main/java/com/ticketing/server/payment/service/PaymentServiceImpl.java index 85e9cb1..1506fbd 100644 --- a/server/src/main/java/com/ticketing/server/payment/service/PaymentServiceImpl.java +++ b/server/src/main/java/com/ticketing/server/payment/service/PaymentServiceImpl.java @@ -1,26 +1,37 @@ package com.ticketing.server.payment.service; import com.ticketing.server.payment.domain.repository.PaymentRepository; -import com.ticketing.server.payment.service.dto.SimplePaymentDto; +import com.ticketing.server.payment.service.dto.SimplePaymentDTO; +import com.ticketing.server.payment.service.dto.SimplePaymentsDTO; import com.ticketing.server.payment.service.interfaces.PaymentService; -import com.ticketing.server.user.api.dto.response.SimplePaymentsResponse; import java.util.stream.Collectors; +import javax.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; @Service @RequiredArgsConstructor +@Transactional(readOnly = true) +@Validated +@Slf4j public class PaymentServiceImpl implements PaymentService { private final PaymentRepository paymentRepository; @Override - public SimplePaymentsResponse findSimplePayments(Long userId) { - return paymentRepository.findByUserId(userId) + public SimplePaymentsDTO findSimplePayments(@NotNull Long userAlternateId) { + return paymentRepository.findByUserAlternateId(userAlternateId) .stream() - .map(SimplePaymentDto::from) - .collect(Collectors.collectingAndThen(Collectors.toList() - , list -> SimplePaymentsResponse.from(userId, list))); + .map(SimplePaymentDTO::new) + .collect(Collectors + .collectingAndThen( + Collectors.toList() + , payments -> new SimplePaymentsDTO(userAlternateId, payments) + ) + ); } } diff --git a/server/src/main/java/com/ticketing/server/payment/service/dto/CreatePaymentDTO.java b/server/src/main/java/com/ticketing/server/payment/service/dto/CreatePaymentDTO.java new file mode 100644 index 0000000..b0ee78f --- /dev/null +++ b/server/src/main/java/com/ticketing/server/payment/service/dto/CreatePaymentDTO.java @@ -0,0 +1,32 @@ +package com.ticketing.server.payment.service.dto; + +import com.ticketing.server.payment.domain.Payment; +import com.ticketing.server.payment.domain.PaymentStatus; +import com.ticketing.server.payment.domain.PaymentType; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class CreatePaymentDTO { + + private Long userAlternateId; + private String movieTitle; + private PaymentType type; + private PaymentStatus status; + private String paymentNumber; + private Integer totalPrice; + + public Payment toEntity() { + return new Payment + ( + this.getUserAlternateId(), + this.getMovieTitle(), + this.getType(), + this.getStatus(), + this.getPaymentNumber(), + this.getTotalPrice() + ); + } + +} diff --git a/server/src/main/java/com/ticketing/server/payment/service/dto/CreatePaymentDto.java b/server/src/main/java/com/ticketing/server/payment/service/dto/CreatePaymentDto.java deleted file mode 100644 index d5bde24..0000000 --- a/server/src/main/java/com/ticketing/server/payment/service/dto/CreatePaymentDto.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.ticketing.server.payment.service.dto; - -import com.ticketing.server.payment.domain.PaymentStatus; -import com.ticketing.server.payment.domain.PaymentType; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class CreatePaymentDto { - - private Long userId; - private String movieTitle; - private PaymentType type; - private PaymentStatus status; - private String paymentNumber; - private Integer totalPrice; - - public static CreatePaymentDto of( - Long userId, - String movieTitle, - PaymentType type, - PaymentStatus status, - String paymentNumber, - Integer totalPrice) { - return new CreatePaymentDto(userId, movieTitle, type, status, paymentNumber, totalPrice); - } - -} diff --git a/server/src/main/java/com/ticketing/server/payment/service/dto/SimplePaymentDto.java b/server/src/main/java/com/ticketing/server/payment/service/dto/SimplePaymentDTO.java similarity index 80% rename from server/src/main/java/com/ticketing/server/payment/service/dto/SimplePaymentDto.java rename to server/src/main/java/com/ticketing/server/payment/service/dto/SimplePaymentDTO.java index 4fdbf8b..9cb7b13 100644 --- a/server/src/main/java/com/ticketing/server/payment/service/dto/SimplePaymentDto.java +++ b/server/src/main/java/com/ticketing/server/payment/service/dto/SimplePaymentDTO.java @@ -7,15 +7,15 @@ import lombok.Getter; @Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) -public class SimplePaymentDto { +public class SimplePaymentDTO { private Long paymentId; private String movieTitle; private String paymentNumber; private Integer totalPrice; - public static SimplePaymentDto from(Payment payment) { - return new SimplePaymentDto( + public SimplePaymentDTO(Payment payment) { + this( payment.getId(), payment.getMovieTitle(), payment.getPaymentNumber(), diff --git a/server/src/main/java/com/ticketing/server/payment/service/dto/SimplePaymentsDTO.java b/server/src/main/java/com/ticketing/server/payment/service/dto/SimplePaymentsDTO.java new file mode 100644 index 0000000..228c118 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/payment/service/dto/SimplePaymentsDTO.java @@ -0,0 +1,19 @@ +package com.ticketing.server.payment.service.dto; + +import com.ticketing.server.payment.application.response.SimplePaymentsResponse; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class SimplePaymentsDTO { + + private final Long userAlternateId; + private final List payments; + + public SimplePaymentsResponse toResponse() { + return new SimplePaymentsResponse(userAlternateId, payments); + } + +} diff --git a/server/src/main/java/com/ticketing/server/payment/service/interfaces/PaymentApisService.java b/server/src/main/java/com/ticketing/server/payment/service/interfaces/PaymentApisService.java new file mode 100644 index 0000000..4705083 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/payment/service/interfaces/PaymentApisService.java @@ -0,0 +1,9 @@ +package com.ticketing.server.payment.service.interfaces; + +import com.ticketing.server.payment.application.response.PaymentDetailResponse; +import javax.validation.constraints.NotNull; + +public interface PaymentApisService { + + PaymentDetailResponse findPaymentDetail(@NotNull Long paymentId); +} diff --git a/server/src/main/java/com/ticketing/server/payment/service/interfaces/PaymentService.java b/server/src/main/java/com/ticketing/server/payment/service/interfaces/PaymentService.java index 076b93b..a66d2ae 100644 --- a/server/src/main/java/com/ticketing/server/payment/service/interfaces/PaymentService.java +++ b/server/src/main/java/com/ticketing/server/payment/service/interfaces/PaymentService.java @@ -1,9 +1,10 @@ package com.ticketing.server.payment.service.interfaces; -import com.ticketing.server.user.api.dto.response.SimplePaymentsResponse; +import com.ticketing.server.payment.service.dto.SimplePaymentsDTO; +import javax.validation.constraints.NotNull; public interface PaymentService { - SimplePaymentsResponse findSimplePayments(Long userId); + SimplePaymentsDTO findSimplePayments(@NotNull Long userAlternateId); } diff --git a/server/src/main/java/com/ticketing/server/user/api/PaymentClient.java b/server/src/main/java/com/ticketing/server/user/api/PaymentClient.java index 8a284eb..dca9372 100644 --- a/server/src/main/java/com/ticketing/server/user/api/PaymentClient.java +++ b/server/src/main/java/com/ticketing/server/user/api/PaymentClient.java @@ -1,10 +1,10 @@ package com.ticketing.server.user.api; -import com.ticketing.server.user.api.dto.request.SimplePaymentsRequest; -import com.ticketing.server.user.api.dto.response.SimplePaymentsResponse; +import com.ticketing.server.payment.application.response.SimplePaymentsResponse; +import javax.validation.constraints.NotNull; public interface PaymentClient { - SimplePaymentsResponse getSimplePayments(SimplePaymentsRequest request); + SimplePaymentsResponse getPayments(@NotNull Long alternateId); } diff --git a/server/src/main/java/com/ticketing/server/user/api/dto/request/SimplePaymentsRequest.java b/server/src/main/java/com/ticketing/server/user/api/dto/request/SimplePaymentsRequest.java deleted file mode 100644 index f86eba6..0000000 --- a/server/src/main/java/com/ticketing/server/user/api/dto/request/SimplePaymentsRequest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.ticketing.server.user.api.dto.request; - -import com.ticketing.server.user.domain.User; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class SimplePaymentsRequest { - - private Long userId; - - public static SimplePaymentsRequest from(User user) { - return new SimplePaymentsRequest(user.getId()); - } - -} diff --git a/server/src/main/java/com/ticketing/server/user/api/dto/response/SimplePaymentsResponse.java b/server/src/main/java/com/ticketing/server/user/api/dto/response/SimplePaymentsResponse.java deleted file mode 100644 index 3e4dfbe..0000000 --- a/server/src/main/java/com/ticketing/server/user/api/dto/response/SimplePaymentsResponse.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.ticketing.server.user.api.dto.response; - -import com.ticketing.server.payment.service.dto.SimplePaymentDto; -import java.util.List; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class SimplePaymentsResponse { - - private Long userId; - - private List payments; - - public static SimplePaymentsResponse from(Long userId, List simplePayments) { - return new SimplePaymentsResponse(userId, simplePayments); - } - -} diff --git a/server/src/main/java/com/ticketing/server/user/api/impl/PaymentClientImpl.java b/server/src/main/java/com/ticketing/server/user/api/impl/PaymentClientImpl.java index b7b5c6d..eb91ec6 100644 --- a/server/src/main/java/com/ticketing/server/user/api/impl/PaymentClientImpl.java +++ b/server/src/main/java/com/ticketing/server/user/api/impl/PaymentClientImpl.java @@ -1,9 +1,10 @@ package com.ticketing.server.user.api.impl; -import com.ticketing.server.payment.service.PaymentServiceImpl; +import com.ticketing.server.payment.application.response.SimplePaymentsResponse; +import com.ticketing.server.payment.service.dto.SimplePaymentsDTO; +import com.ticketing.server.payment.service.interfaces.PaymentService; import com.ticketing.server.user.api.PaymentClient; -import com.ticketing.server.user.api.dto.request.SimplePaymentsRequest; -import com.ticketing.server.user.api.dto.response.SimplePaymentsResponse; +import javax.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -11,11 +12,12 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class PaymentClientImpl implements PaymentClient { - private final PaymentServiceImpl paymentService; + private final PaymentService paymentService; @Override - public SimplePaymentsResponse getSimplePayments(SimplePaymentsRequest request) { - return paymentService.findSimplePayments(request.getUserId()); + public SimplePaymentsResponse getPayments(@NotNull Long alternateId) { + SimplePaymentsDTO payments = paymentService.findSimplePayments(alternateId); + return payments.toResponse(); } } diff --git a/server/src/main/java/com/ticketing/server/user/application/UserController.java b/server/src/main/java/com/ticketing/server/user/application/UserController.java index 4639dab..46edf5b 100644 --- a/server/src/main/java/com/ticketing/server/user/application/UserController.java +++ b/server/src/main/java/com/ticketing/server/user/application/UserController.java @@ -3,14 +3,16 @@ package com.ticketing.server.user.application; import com.ticketing.server.user.application.request.SignUpRequest; import com.ticketing.server.user.application.request.UserChangePasswordRequest; import com.ticketing.server.user.application.request.UserDeleteRequest; +import com.ticketing.server.user.application.response.PaymentsResponse; import com.ticketing.server.user.application.response.SignUpResponse; -import com.ticketing.server.user.application.response.SimplePaymentDetailsResponse; import com.ticketing.server.user.application.response.UserChangePasswordResponse; import com.ticketing.server.user.application.response.UserDeleteResponse; import com.ticketing.server.user.application.response.UserDetailResponse; import com.ticketing.server.user.domain.User; import com.ticketing.server.user.domain.UserGrade; -import com.ticketing.server.user.service.UserServiceImpl; +import com.ticketing.server.user.service.dto.UserDetailDTO; +import com.ticketing.server.user.service.interfaces.UserApisService; +import com.ticketing.server.user.service.interfaces.UserService; import javax.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -34,7 +36,8 @@ import org.springframework.web.bind.annotation.RestController; @Slf4j public class UserController { - private final UserServiceImpl userService; + private final UserApisService userApisService; + private final UserService userService; private final PasswordEncoder passwordEncoder; @PostMapping @@ -44,21 +47,21 @@ public class UserController { } @GetMapping("/details") - @Secured("ROLE_GUEST") + @Secured(UserGrade.ROLES.USER) public ResponseEntity details(@AuthenticationPrincipal UserDetails userRequest) { - User user = userService.findByEmail(userRequest.getUsername()); - return ResponseEntity.status(HttpStatus.OK).body(UserDetailResponse.from(user)); + UserDetailDTO userDetail = userService.findDetailByEmail(userRequest.getUsername()); + return ResponseEntity.status(HttpStatus.OK).body(userDetail.toResponse()); } @DeleteMapping - @Secured(UserGrade.ROLES.GUEST) + @Secured(UserGrade.ROLES.USER) public ResponseEntity deleteUser(@RequestBody @Valid UserDeleteRequest request) { User user = userService.delete(request.toDeleteUserDto(passwordEncoder)); return ResponseEntity.status(HttpStatus.OK).body(UserDeleteResponse.from(user)); } @PutMapping("/password") - @Secured(UserGrade.ROLES.GUEST) + @Secured(UserGrade.ROLES.USER) public ResponseEntity changePassword( @AuthenticationPrincipal UserDetails userRequest, @RequestBody @Valid UserChangePasswordRequest request) { @@ -67,9 +70,9 @@ public class UserController { } @GetMapping("/payments") - @Secured("ROLE_GUEST") - public ResponseEntity getPayments(@AuthenticationPrincipal UserDetails userRequest) { - SimplePaymentDetailsResponse paymentDetails = userService.findSimplePaymentDetails(userRequest.getUsername()); + @Secured(UserGrade.ROLES.USER) + public ResponseEntity getPayments(@AuthenticationPrincipal UserDetails userRequest) { + PaymentsResponse paymentDetails = userApisService.findPaymentsByEmail(userRequest.getUsername()); return ResponseEntity.status(HttpStatus.OK).body(paymentDetails); } diff --git a/server/src/main/java/com/ticketing/server/user/application/response/PaymentsResponse.java b/server/src/main/java/com/ticketing/server/user/application/response/PaymentsResponse.java new file mode 100644 index 0000000..9daffb0 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/user/application/response/PaymentsResponse.java @@ -0,0 +1,20 @@ +package com.ticketing.server.user.application.response; + +import com.ticketing.server.payment.application.response.SimplePaymentsResponse; +import com.ticketing.server.payment.service.dto.SimplePaymentDTO; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class PaymentsResponse { + + private String email; + private List payments; + + public PaymentsResponse(String email, SimplePaymentsResponse simplePayments) { + this(email, simplePayments.getPayments()); + } + +} diff --git a/server/src/main/java/com/ticketing/server/user/application/response/SimplePaymentDetailsResponse.java b/server/src/main/java/com/ticketing/server/user/application/response/SimplePaymentDetailsResponse.java deleted file mode 100644 index 2988e5f..0000000 --- a/server/src/main/java/com/ticketing/server/user/application/response/SimplePaymentDetailsResponse.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.ticketing.server.user.application.response; - -import com.ticketing.server.payment.service.dto.SimplePaymentDto; -import com.ticketing.server.user.api.dto.response.SimplePaymentsResponse; -import java.util.List; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class SimplePaymentDetailsResponse { - - private String email; - private List payments; - - public static SimplePaymentDetailsResponse of(String email, SimplePaymentsResponse paymentsResponse) { - return new SimplePaymentDetailsResponse(email, paymentsResponse.getPayments()); - } - -} diff --git a/server/src/main/java/com/ticketing/server/user/application/response/UserDetailResponse.java b/server/src/main/java/com/ticketing/server/user/application/response/UserDetailResponse.java index 5f3cb2c..b25eab7 100644 --- a/server/src/main/java/com/ticketing/server/user/application/response/UserDetailResponse.java +++ b/server/src/main/java/com/ticketing/server/user/application/response/UserDetailResponse.java @@ -1,21 +1,27 @@ package com.ticketing.server.user.application.response; -import com.ticketing.server.user.domain.User; import com.ticketing.server.user.domain.UserGrade; +import com.ticketing.server.user.service.dto.UserDetailDTO; +import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; @Getter -@AllArgsConstructor +@AllArgsConstructor(access = AccessLevel.PRIVATE) public class UserDetailResponse { - private String name; - private String email; - private UserGrade grade; - private String phone; + private final String name; + private final String email; + private final UserGrade grade; + private final String phone; - public static UserDetailResponse from(User user) { - return new UserDetailResponse(user.getName(), user.getEmail(), user.getGrade(), user.getPhone()); + public UserDetailResponse(UserDetailDTO userDetailDto) { + this( + userDetailDto.getName(), + userDetailDto.getEmail(), + userDetailDto.getGrade(), + userDetailDto.getPhone() + ); } } diff --git a/server/src/main/java/com/ticketing/server/user/domain/SequenceGenerator.java b/server/src/main/java/com/ticketing/server/user/domain/SequenceGenerator.java new file mode 100644 index 0000000..82cce95 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/user/domain/SequenceGenerator.java @@ -0,0 +1,9 @@ +package com.ticketing.server.user.domain; + +public interface SequenceGenerator { + + Long generateId(); + + long[] parse(long id); + +} diff --git a/server/src/main/java/com/ticketing/server/user/domain/Snowflake.java b/server/src/main/java/com/ticketing/server/user/domain/Snowflake.java new file mode 100644 index 0000000..f120057 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/user/domain/Snowflake.java @@ -0,0 +1,118 @@ +package com.ticketing.server.user.domain; + +import java.net.NetworkInterface; +import java.security.SecureRandom; +import java.time.Instant; +import java.util.Enumeration; +import org.springframework.stereotype.Component; + +/** + * Twitter Snowflake Custom. + * https://github.com/callicoder/java-snowflake + */ +@Component +public class Snowflake implements SequenceGenerator { + + private static final int EPOCH_BITS = 41; + private static final int NODE_ID_BITS = 10; + private static final int SEQUENCE_BITS = 12; + + private static final long MAX_NODE_ID = (1L << NODE_ID_BITS) - 1; + private static final long MAX_SEQUENCE = (1L << SEQUENCE_BITS) - 1; + + // Custom Epoch (January 1, 2015 Midnight UTC = 2015-01-01T00:00:00Z) + private static final long DEFAULT_CUSTOM_EPOCH = 1420070400000L; + + private final long nodeId; + private final long customEpoch; + + private volatile long lastTimestamp = -1L; + private volatile long sequence = 0L; + + // Let Snowflake generate a nodeId + public Snowflake() { + this.nodeId = createNodeId(); + this.customEpoch = DEFAULT_CUSTOM_EPOCH; + } + + @Override + public Long generateId() { + return nextId(); + } + + private synchronized long nextId() { + long currentTimestamp = timestamp(); + + if(currentTimestamp < lastTimestamp) { + throw new IllegalStateException("Invalid System Clock!"); + } + + if (currentTimestamp == lastTimestamp) { + sequence = (sequence + 1) & MAX_SEQUENCE; + if(sequence == 0) { + // 동일 밀리초 시퀀스가 소진됐을 경우 대기. + currentTimestamp = waitNextMillis(currentTimestamp); + } + } else { + // 다음 밀리초 시퀀스 초기화. + sequence = 0; + } + + lastTimestamp = currentTimestamp; + return currentTimestamp << (NODE_ID_BITS + SEQUENCE_BITS) + | (nodeId << SEQUENCE_BITS) + | sequence; + } + + public long[] parse(long id) { + long maskNodeId = ((1L << NODE_ID_BITS) - 1) << SEQUENCE_BITS; + long maskSequence = (1L << SEQUENCE_BITS) - 1; + + long timestamp = (id >> (NODE_ID_BITS + SEQUENCE_BITS)) + customEpoch; + long nodeId = (id & maskNodeId) >> SEQUENCE_BITS; + long sequence = id & maskSequence; + + return new long[]{timestamp, nodeId, sequence}; + } + + private long timestamp() { + return Instant.now().toEpochMilli() - customEpoch; + } + + private long waitNextMillis(long currentTimestamp) { + while (currentTimestamp == lastTimestamp) { + currentTimestamp = timestamp(); + } + return currentTimestamp; + } + + private long createNodeId() { + long nodeId; + try { + StringBuilder sb = new StringBuilder(); + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + NetworkInterface networkInterface = networkInterfaces.nextElement(); + byte[] mac = networkInterface.getHardwareAddress(); + if (mac != null) { + for(byte macPort: mac) { + sb.append(String.format("%02X", macPort)); + } + } + } + nodeId = sb.toString().hashCode(); + } catch (Exception ex) { + nodeId = (new SecureRandom().nextInt()); + } + nodeId = nodeId & MAX_NODE_ID; + return nodeId; + } + + @Override + public String toString() { + return "Snowflake Settings [EPOCH_BITS=" + EPOCH_BITS + ", NODE_ID_BITS=" + NODE_ID_BITS + + ", SEQUENCE_BITS=" + SEQUENCE_BITS + ", CUSTOM_EPOCH=" + customEpoch + + ", NodeId=" + nodeId + "]"; + } + +} diff --git a/server/src/main/java/com/ticketing/server/user/domain/User.java b/server/src/main/java/com/ticketing/server/user/domain/User.java index 2936ffe..44c4732 100644 --- a/server/src/main/java/com/ticketing/server/user/domain/User.java +++ b/server/src/main/java/com/ticketing/server/user/domain/User.java @@ -14,42 +14,54 @@ import javax.persistence.Enumerated; import javax.validation.constraints.Email; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; +import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @Entity @Getter -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class User extends AbstractEntity { - @Column(name = "name") + @Column(name = "alternate_key", unique = true, nullable = false) + @NotNull(message = "{validation.not.null.alternate.key}") + private Long alternateId; + + @Column(name = "name", nullable = false) @NotEmpty(message = "{validation.not.empty.name}") private String name; - @Column(name = "email") + @Column(name = "email", unique = true, nullable = false) @NotEmpty(message = "{validation.not.empty.email}") @Email(message = "{validation.email}") private String email; - @Column(name = "password") + @Column(name = "password", nullable = false) @NotEmpty(message = "{validation.not.empty.password}") private String password; - @Column(name = "grade") - @NotNull(message = "{validation.not.empty.grade}") + @Column(name = "grade", nullable = false) + @NotNull(message = "{validation.not.null.grade}") @Enumerated(value = EnumType.STRING) - private UserGrade grade = UserGrade.GUEST; + private UserGrade grade = UserGrade.USER; - @Column(name = "phone") + @Column(name = "phone", nullable = false) @NotEmpty(message = "{validation.not.empty.phone}") @Phone private String phone; - private boolean isDeleted = false; + public User(Long alternateId, String name, String email, String password, UserGrade grade, String phone) { + this.alternateId = alternateId; + this.name = name; + this.email = email; + this.password = password; + this.grade = grade; + this.phone = phone; + } - private LocalDateTime deletedAt; - - public User(String name, String email, String password, UserGrade grade, String phone) { + User(Long id, Long alternateId, String name, String email, String password, UserGrade grade, String phone) { + this.id = id; + this.alternateId = alternateId; this.name = name; this.email = email; this.password = password; @@ -58,13 +70,12 @@ public class User extends AbstractEntity { } public User delete(DeleteUserDTO deleteUser) { - if (isDeleted) { + if (deletedAt != null) { throw ErrorCode.throwDeletedEmail(); } checkPassword(deleteUser); - isDeleted = true; deletedAt = LocalDateTime.now(); return this; } diff --git a/server/src/main/java/com/ticketing/server/user/domain/UserGrade.java b/server/src/main/java/com/ticketing/server/user/domain/UserGrade.java index cc19ab5..7184d4f 100644 --- a/server/src/main/java/com/ticketing/server/user/domain/UserGrade.java +++ b/server/src/main/java/com/ticketing/server/user/domain/UserGrade.java @@ -6,7 +6,7 @@ import lombok.RequiredArgsConstructor; public enum UserGrade { ADMIN(ROLES.ADMIN, null), STAFF(ROLES.STAFF, ROLES.ADMIN), - GUEST(ROLES.GUEST, ROLES.STAFF); + USER(ROLES.USER, ROLES.STAFF); private final String roleName; private final String parentName; @@ -15,7 +15,7 @@ public enum UserGrade { public static final String ADMIN = "ROLE_ADMIN"; public static final String STAFF = "ROLE_STAFF"; - public static final String GUEST = "ROLE_GUEST"; + public static final String USER = "ROLE_USER"; private ROLES() { } diff --git a/server/src/main/java/com/ticketing/server/user/domain/repository/UserRepository.java b/server/src/main/java/com/ticketing/server/user/domain/repository/UserRepository.java index adec774..700d2a7 100644 --- a/server/src/main/java/com/ticketing/server/user/domain/repository/UserRepository.java +++ b/server/src/main/java/com/ticketing/server/user/domain/repository/UserRepository.java @@ -10,6 +10,6 @@ public interface UserRepository extends JpaRepository { Optional findByEmail(String email); - Optional findByEmailAndIsDeletedFalse(String email); + Optional findByEmailAndDeletedAtNull(String email); } diff --git a/server/src/main/java/com/ticketing/server/user/service/CustomUserDetailsService.java b/server/src/main/java/com/ticketing/server/user/service/CustomUserDetailsService.java index 0ba78f2..3a819c2 100644 --- a/server/src/main/java/com/ticketing/server/user/service/CustomUserDetailsService.java +++ b/server/src/main/java/com/ticketing/server/user/service/CustomUserDetailsService.java @@ -19,7 +19,7 @@ public class CustomUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { - return userRepository.findByEmailAndIsDeletedFalse(email) + return userRepository.findByEmailAndDeletedAtNull(email) .map(this::createUserDetails) .orElseThrow(ErrorCode::throwEmailNotFound); } @@ -33,4 +33,5 @@ public class CustomUserDetailsService implements UserDetailsService { , Collections.singleton(grantedAuthority) ); } + } diff --git a/server/src/main/java/com/ticketing/server/user/service/UserApisServiceImpl.java b/server/src/main/java/com/ticketing/server/user/service/UserApisServiceImpl.java new file mode 100644 index 0000000..c8c90a8 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/user/service/UserApisServiceImpl.java @@ -0,0 +1,34 @@ +package com.ticketing.server.user.service; + +import com.ticketing.server.payment.application.response.SimplePaymentsResponse; +import com.ticketing.server.user.api.PaymentClient; +import com.ticketing.server.user.application.response.PaymentsResponse; +import com.ticketing.server.user.domain.User; +import com.ticketing.server.user.service.interfaces.UserApisService; +import com.ticketing.server.user.service.interfaces.UserService; +import javax.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +@Validated +@Slf4j +public class UserApisServiceImpl implements UserApisService { + + private final PaymentClient paymentClient; + private final UserService userService; + + @Override + public PaymentsResponse findPaymentsByEmail(@NotNull String email) { + User user = userService.findNotDeletedUserByEmail(email); + SimplePaymentsResponse simplePayments = paymentClient.getPayments(user.getAlternateId()); + + return new PaymentsResponse(user.getEmail(), simplePayments); + } + +} diff --git a/server/src/main/java/com/ticketing/server/user/service/UserServiceImpl.java b/server/src/main/java/com/ticketing/server/user/service/UserServiceImpl.java index 07f1202..39b8a98 100644 --- a/server/src/main/java/com/ticketing/server/user/service/UserServiceImpl.java +++ b/server/src/main/java/com/ticketing/server/user/service/UserServiceImpl.java @@ -1,18 +1,17 @@ package com.ticketing.server.user.service; import com.ticketing.server.global.exception.ErrorCode; -import com.ticketing.server.user.api.PaymentClient; -import com.ticketing.server.user.api.dto.request.SimplePaymentsRequest; -import com.ticketing.server.user.api.dto.response.SimplePaymentsResponse; -import com.ticketing.server.user.application.response.SimplePaymentDetailsResponse; +import com.ticketing.server.user.domain.SequenceGenerator; import com.ticketing.server.user.domain.User; import com.ticketing.server.user.domain.repository.UserRepository; import com.ticketing.server.user.service.dto.ChangePasswordDTO; import com.ticketing.server.user.service.dto.DeleteUserDTO; import com.ticketing.server.user.service.dto.SignUpDTO; +import com.ticketing.server.user.service.dto.UserDetailDTO; import com.ticketing.server.user.service.interfaces.UserService; import java.util.Optional; import javax.validation.Valid; +import javax.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -27,14 +26,14 @@ import org.springframework.validation.annotation.Validated; public class UserServiceImpl implements UserService { private final UserRepository userRepository; - private final PaymentClient paymentClient; + private final SequenceGenerator sequenceGenerator; @Override @Transactional public User register(@Valid SignUpDTO signUpDto) { Optional user = userRepository.findByEmail(signUpDto.getEmail()); if (user.isEmpty()) { - return userRepository.save(signUpDto.toUser()); + return userRepository.save(signUpDto.toUser(sequenceGenerator.generateId())); } throw ErrorCode.throwDuplicateEmail(); @@ -55,21 +54,16 @@ public class UserServiceImpl implements UserService { } @Override - public User findByEmail(String email) { - return userRepository.findByEmail(email) + public UserDetailDTO findDetailByEmail(@NotNull String email) { + User user = userRepository.findByEmail(email) .orElseThrow(ErrorCode::throwEmailNotFound); + + return new UserDetailDTO(user); } @Override - public SimplePaymentDetailsResponse findSimplePaymentDetails(String email) { - User user = findNotDeletedUserByEmail(email); - SimplePaymentsResponse simplePayments = paymentClient.getSimplePayments(SimplePaymentsRequest.from(user)); - - return SimplePaymentDetailsResponse.of(email, simplePayments); - } - - private User findNotDeletedUserByEmail(String email) { - return userRepository.findByEmailAndIsDeletedFalse(email) + public User findNotDeletedUserByEmail(@NotNull String email) { + return userRepository.findByEmailAndDeletedAtNull(email) .orElseThrow(ErrorCode::throwEmailNotFound); } diff --git a/server/src/main/java/com/ticketing/server/user/service/dto/SignUpDTO.java b/server/src/main/java/com/ticketing/server/user/service/dto/SignUpDTO.java index 2dfc484..37742f1 100644 --- a/server/src/main/java/com/ticketing/server/user/service/dto/SignUpDTO.java +++ b/server/src/main/java/com/ticketing/server/user/service/dto/SignUpDTO.java @@ -5,9 +5,11 @@ import com.ticketing.server.user.domain.User; import com.ticketing.server.user.domain.UserGrade; import javax.validation.constraints.Email; import javax.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; import lombok.Getter; @Getter +@AllArgsConstructor public class SignUpDTO { @NotEmpty(message = "{validation.not.empty.name}") @@ -24,15 +26,8 @@ public class SignUpDTO { @Phone private String phone; - public SignUpDTO(String name, String email, String password, String phone) { - this.name = name; - this.email = email; - this.password = password; - this.phone = phone; - } - - public User toUser() { - return new User(this.name, this.email, password, UserGrade.GUEST, this.phone); + public User toUser(long alternateId) { + return new User(alternateId, this.name, this.email, password, UserGrade.USER, this.phone); } @Override diff --git a/server/src/main/java/com/ticketing/server/user/service/dto/UserDetailDTO.java b/server/src/main/java/com/ticketing/server/user/service/dto/UserDetailDTO.java new file mode 100644 index 0000000..778d5be --- /dev/null +++ b/server/src/main/java/com/ticketing/server/user/service/dto/UserDetailDTO.java @@ -0,0 +1,33 @@ +package com.ticketing.server.user.service.dto; + +import com.ticketing.server.user.application.response.UserDetailResponse; +import com.ticketing.server.user.domain.User; +import com.ticketing.server.user.domain.UserGrade; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class UserDetailDTO { + + private Long alternateId; + private String name; + private String email; + private UserGrade grade; + private String phone; + + public UserDetailDTO(User user) { + this( + user.getAlternateId(), + user.getName(), + user.getEmail(), + user.getGrade(), + user.getPhone() + ); + } + + public UserDetailResponse toResponse() { + return new UserDetailResponse(this); + } +} diff --git a/server/src/main/java/com/ticketing/server/user/service/interfaces/UserApisService.java b/server/src/main/java/com/ticketing/server/user/service/interfaces/UserApisService.java new file mode 100644 index 0000000..311da72 --- /dev/null +++ b/server/src/main/java/com/ticketing/server/user/service/interfaces/UserApisService.java @@ -0,0 +1,10 @@ +package com.ticketing.server.user.service.interfaces; + +import com.ticketing.server.user.application.response.PaymentsResponse; +import javax.validation.constraints.NotNull; + +public interface UserApisService { + + PaymentsResponse findPaymentsByEmail(@NotNull String email); + +} diff --git a/server/src/main/java/com/ticketing/server/user/service/interfaces/UserService.java b/server/src/main/java/com/ticketing/server/user/service/interfaces/UserService.java index 223a2c0..adc998f 100644 --- a/server/src/main/java/com/ticketing/server/user/service/interfaces/UserService.java +++ b/server/src/main/java/com/ticketing/server/user/service/interfaces/UserService.java @@ -1,11 +1,12 @@ package com.ticketing.server.user.service.interfaces; -import com.ticketing.server.user.application.response.SimplePaymentDetailsResponse; import com.ticketing.server.user.domain.User; import com.ticketing.server.user.service.dto.ChangePasswordDTO; import com.ticketing.server.user.service.dto.DeleteUserDTO; import com.ticketing.server.user.service.dto.SignUpDTO; +import com.ticketing.server.user.service.dto.UserDetailDTO; import javax.validation.Valid; +import javax.validation.constraints.NotNull; public interface UserService { @@ -15,8 +16,8 @@ public interface UserService { User changePassword(@Valid ChangePasswordDTO changePasswordDto); - User findByEmail(String email); + UserDetailDTO findDetailByEmail(@NotNull String email); - SimplePaymentDetailsResponse findSimplePaymentDetails(String email); + User findNotDeletedUserByEmail(@NotNull String email); } diff --git a/server/src/main/resources/i18n/messages.properties b/server/src/main/resources/i18n/messages.properties index 0f011da..ac061b6 100644 --- a/server/src/main/resources/i18n/messages.properties +++ b/server/src/main/resources/i18n/messages.properties @@ -3,8 +3,11 @@ validation.not.empty.email=\uC774\uBA54\uC77C\uC740 \uD544\uC218 \uC785\uB2C8\uB validation.not.empty.password=\uD328\uC2A4\uC6CC\uB4DC\uB294 \uD544\uC218 \uC785\uB2C8\uB2E4. validation.not.empty.oldpassword=\uD604\uC7AC \uD328\uC2A4\uC6CC\uB4DC\uB294 \uD544\uC218 \uC785\uB2C8\uB2E4. validation.not.empty.newpassword=\uBCC0\uACBD\uD560 \uD328\uC2A4\uC6CC\uB4DC\uB294 \uD544\uC218 \uC785\uB2C8\uB2E4. -validation.not.empty.grade=\uC0AC\uC6A9\uC790 \uB4F1\uAE09\uC740 \uD544\uC218 \uC785\uB2C8\uB2E4. validation.not.empty.phone=\uD734\uB300\uBC88\uD638\uB294 \uD544\uC218 \uC785\uB2C8\uB2E4. +validation.not.null.grade=\uC0AC\uC6A9\uC790 \uB4F1\uAE09\uC740 \uD544\uC218 \uC785\uB2C8\uB2E4. +validation.not.null.payment.id=\uACB0\uC81C ID\uB294 \uD544\uC218 \uC785\uB2C8\uB2E4. +validation.not.null.alternate.key=\uB300\uCCB4\uD0A4\uB294 \uD544\uC218 \uC785\uB2C8\uB2E4. validation.email=\uC774\uBA54\uC77C\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. validation.phone=\uD734\uB300\uBC88\uD638\uAC00 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. validation.password.not.change=\uB3D9\uC77C\uD55C \uD328\uC2A4\uC6CC\uB4DC\uB85C \uBCC0\uACBD\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. + diff --git a/server/src/main/resources/i18n/messages_en.properties b/server/src/main/resources/i18n/messages_en.properties index 1972374..2dcf346 100644 --- a/server/src/main/resources/i18n/messages_en.properties +++ b/server/src/main/resources/i18n/messages_en.properties @@ -3,8 +3,10 @@ validation.not.empty.email=email is required. validation.not.empty.password=password is required. validation.not.empty.oldpassword=Old Password is required. validation.not.empty.newpassword=New Password is required. -validation.not.empty.grade=user grade is required. validation.not.empty.phone=phone is required. +validation.not.null.grade=user grade is required. +validation.not.null.payment.id=payment id is required. +validation.not.null.alternate.key=alternate Key is required. validation.email=email is not valid. validation.phone=phone is not valid. validation.password.not.change=password not change. diff --git a/server/src/main/resources/i18n/messages_ko.properties b/server/src/main/resources/i18n/messages_ko.properties index 0f011da..ac061b6 100644 --- a/server/src/main/resources/i18n/messages_ko.properties +++ b/server/src/main/resources/i18n/messages_ko.properties @@ -3,8 +3,11 @@ validation.not.empty.email=\uC774\uBA54\uC77C\uC740 \uD544\uC218 \uC785\uB2C8\uB validation.not.empty.password=\uD328\uC2A4\uC6CC\uB4DC\uB294 \uD544\uC218 \uC785\uB2C8\uB2E4. validation.not.empty.oldpassword=\uD604\uC7AC \uD328\uC2A4\uC6CC\uB4DC\uB294 \uD544\uC218 \uC785\uB2C8\uB2E4. validation.not.empty.newpassword=\uBCC0\uACBD\uD560 \uD328\uC2A4\uC6CC\uB4DC\uB294 \uD544\uC218 \uC785\uB2C8\uB2E4. -validation.not.empty.grade=\uC0AC\uC6A9\uC790 \uB4F1\uAE09\uC740 \uD544\uC218 \uC785\uB2C8\uB2E4. validation.not.empty.phone=\uD734\uB300\uBC88\uD638\uB294 \uD544\uC218 \uC785\uB2C8\uB2E4. +validation.not.null.grade=\uC0AC\uC6A9\uC790 \uB4F1\uAE09\uC740 \uD544\uC218 \uC785\uB2C8\uB2E4. +validation.not.null.payment.id=\uACB0\uC81C ID\uB294 \uD544\uC218 \uC785\uB2C8\uB2E4. +validation.not.null.alternate.key=\uB300\uCCB4\uD0A4\uB294 \uD544\uC218 \uC785\uB2C8\uB2E4. validation.email=\uC774\uBA54\uC77C\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. validation.phone=\uD734\uB300\uBC88\uD638\uAC00 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. validation.password.not.change=\uB3D9\uC77C\uD55C \uD328\uC2A4\uC6CC\uB4DC\uB85C \uBCC0\uACBD\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. + diff --git a/server/src/test/java/com/ticketing/server/global/security/jwt/TokenProviderTest.java b/server/src/test/java/com/ticketing/server/global/security/jwt/TokenProviderTest.java index df81114..cd94555 100644 --- a/server/src/test/java/com/ticketing/server/global/security/jwt/TokenProviderTest.java +++ b/server/src/test/java/com/ticketing/server/global/security/jwt/TokenProviderTest.java @@ -38,7 +38,7 @@ class TokenProviderTest { @DisplayName("토큰 생성 성공") void createTokenSuccess() { // given - SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(UserGrade.GUEST.name()); + SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(UserGrade.USER.name()); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("ticketing@gmail.com", "123456", Collections.singleton(grantedAuthority)); @@ -53,7 +53,7 @@ class TokenProviderTest { @DisplayName("토큰 복호화 성공") void getAuthentication() { // given - SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(UserGrade.GUEST.name()); + SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(UserGrade.USER.name()); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("ticketing@gmail.com", "123456", Collections.singleton(grantedAuthority)); diff --git a/server/src/test/java/com/ticketing/server/movie/domain/MovieTest.java b/server/src/test/java/com/ticketing/server/movie/domain/MovieTest.java new file mode 100644 index 0000000..3f41f4e --- /dev/null +++ b/server/src/test/java/com/ticketing/server/movie/domain/MovieTest.java @@ -0,0 +1,20 @@ +package com.ticketing.server.movie.domain; + +import java.util.Arrays; +import java.util.List; + +class MovieTest { + + public static final List MOVIES; + + static { + MOVIES = Arrays.asList( + new Movie(1L, "탑건: 매버릭", 130L), + new Movie(2L, "헤어질 결심", 138L), + new Movie(3L, "마녀2", 137L), + new Movie(4L, "범죄도시2", 106L), + new Movie(5L, "버즈 라이트이어", 105L) + ); + } + +} diff --git a/server/src/test/java/com/ticketing/server/movie/domain/MovieTimeTest.java b/server/src/test/java/com/ticketing/server/movie/domain/MovieTimeTest.java index bd451cb..0d84a48 100644 --- a/server/src/test/java/com/ticketing/server/movie/domain/MovieTimeTest.java +++ b/server/src/test/java/com/ticketing/server/movie/domain/MovieTimeTest.java @@ -1,14 +1,49 @@ package com.ticketing.server.movie.domain; +import static com.ticketing.server.movie.domain.MovieTest.MOVIES; +import static com.ticketing.server.movie.domain.TheaterTest.THEATERS; import static org.assertj.core.api.Assertions.assertThat; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; class MovieTimeTest { + public static final List MOVIE_TIMES; + + static { + MOVIE_TIMES = new ArrayList<>(); + + LocalDateTime now = LocalDateTime.now(); + List startAts = Arrays.asList( + LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 8, 0), + LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 10, 0), + LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 12, 0), + LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 14, 0), + LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 16, 0), + LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 18, 0), + LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 21, 0), + LocalDateTime.of(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), 23, 0) + ); + + Long idx = 1L; + for (Theater theater : THEATERS) { + MOVIE_TIMES.add(new MovieTime(idx++, MOVIES.get(0), theater, 1, startAts.get(0))); + MOVIE_TIMES.add(new MovieTime(idx++, MOVIES.get(0), theater, 3, startAts.get(1))); + MOVIE_TIMES.add(new MovieTime(idx++, MOVIES.get(1), theater, 2, startAts.get(2))); + MOVIE_TIMES.add(new MovieTime(idx++, MOVIES.get(2), theater, 4, startAts.get(3))); + MOVIE_TIMES.add(new MovieTime(idx++, MOVIES.get(0), theater, 5, startAts.get(4))); + MOVIE_TIMES.add(new MovieTime(idx++, MOVIES.get(3), theater, 6, startAts.get(5))); + MOVIE_TIMES.add(new MovieTime(idx++, MOVIES.get(0), theater, 7, startAts.get(6))); + MOVIE_TIMES.add(new MovieTime(idx++, MOVIES.get(4), theater, 8, startAts.get(7))); + } + } + @ParameterizedTest @DisplayName("영화상영시간 생성") @ValueSource(longs = {130L, 140L, 30L}) @@ -19,7 +54,7 @@ class MovieTimeTest { LocalDateTime startAt = LocalDateTime.of(2022, 7, 4, 8, 10); // when - MovieTime movieTime = MovieTime.of(movie, theater, 1, startAt); + MovieTime movieTime = new MovieTime(movie, theater, 1, startAt); // then assertThat(movieTime.getEndAt()).isEqualTo(startAt.plusMinutes(runningTime)); diff --git a/server/src/test/java/com/ticketing/server/movie/domain/SeatTest.java b/server/src/test/java/com/ticketing/server/movie/domain/SeatTest.java new file mode 100644 index 0000000..c013dd5 --- /dev/null +++ b/server/src/test/java/com/ticketing/server/movie/domain/SeatTest.java @@ -0,0 +1,27 @@ +package com.ticketing.server.movie.domain; + +import static com.ticketing.server.movie.domain.TheaterTest.THEATERS; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class SeatTest { + + public static final Map> SEATS_BY_THEATER = new HashMap<>(); + + static { + Long idx = 1L; + for (Theater theater : THEATERS) { + List seats = new ArrayList<>(); + for (int col = 1; col <= 2; col++) { + for (int row = 1; row <= 10; row++) { + seats.add(new Seat(idx++, col, row, theater)); + } + } + SEATS_BY_THEATER.put(theater, seats); + } + } + +} diff --git a/server/src/test/java/com/ticketing/server/movie/domain/TheaterTest.java b/server/src/test/java/com/ticketing/server/movie/domain/TheaterTest.java new file mode 100644 index 0000000..159edf0 --- /dev/null +++ b/server/src/test/java/com/ticketing/server/movie/domain/TheaterTest.java @@ -0,0 +1,19 @@ +package com.ticketing.server.movie.domain; + +import java.util.Arrays; +import java.util.List; + +class TheaterTest { + + public static final List THEATERS; + + static { + THEATERS = Arrays.asList( + new Theater(1L, 1), + new Theater(2L, 2), + new Theater(3L, 3), + new Theater(4L, 4) + ); + } + +} diff --git a/server/src/test/java/com/ticketing/server/movie/domain/TicketTest.java b/server/src/test/java/com/ticketing/server/movie/domain/TicketTest.java new file mode 100644 index 0000000..d1cf9fb --- /dev/null +++ b/server/src/test/java/com/ticketing/server/movie/domain/TicketTest.java @@ -0,0 +1,68 @@ +package com.ticketing.server.movie.domain; + +import static com.ticketing.server.movie.domain.MovieTimeTest.MOVIE_TIMES; +import static com.ticketing.server.movie.domain.SeatTest.SEATS_BY_THEATER; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.ticketing.server.global.exception.ErrorCode; +import com.ticketing.server.global.exception.TicketingException; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class TicketTest { + + List tickets; + + @BeforeEach + void init() { + tickets = setupTickets(); + } + + public static List setupTickets() { + List tickets = new ArrayList<>(); + Integer ticketPrice = 15_000; + + Long id = 1L; + for (MovieTime movieTime : MOVIE_TIMES) { + List seats = SEATS_BY_THEATER.get(movieTime.getTheater()); + for (Seat seat : seats) { + tickets.add(new Ticket(id++, seat, movieTime, ticketPrice)); + } + } + return tickets; + } + + @Test + @DisplayName("티켓에 결제 ID 등록 성공") + void registerPaymentSuccess() { + // given + Ticket ticket = tickets.get(0); + + // when + ticket.registerPayment(1L); + + // then + assertThat(ticket.getPaymentId()).isEqualTo(1L); + } + + @Test + @DisplayName("티켓에 결제 ID 등록 실패 - 이미 등록된 경우") + void registerPaymentFail_Duplicate() { + // given + Ticket ticket = tickets.get(0); + + // when + ticket.registerPayment(1L); + + // then + assertThatThrownBy(() -> ticket.registerPayment(1L)) + .isInstanceOf(TicketingException.class) + .extracting("errorCode") + .isEqualTo(ErrorCode.DUPLICATE_PAYMENT); + } + +} diff --git a/server/src/test/java/com/ticketing/server/movie/service/MovieTimeServiceImplTest.java b/server/src/test/java/com/ticketing/server/movie/service/MovieTimeServiceImplTest.java index c6236e2..68d1e32 100644 --- a/server/src/test/java/com/ticketing/server/movie/service/MovieTimeServiceImplTest.java +++ b/server/src/test/java/com/ticketing/server/movie/service/MovieTimeServiceImplTest.java @@ -63,7 +63,7 @@ public class MovieTimeServiceImplTest { // given Movie movie = new Movie(title, 106L); Theater theater = new Theater(1); - MovieTime movieTime = MovieTime.of(movie, theater, 1, + MovieTime movieTime = new MovieTime(movie, theater, 1, LocalDateTime.of(2022, 7, 1, 17, 0, 0) ); diff --git a/server/src/test/java/com/ticketing/server/movie/service/TicketServiceImplTest.java b/server/src/test/java/com/ticketing/server/movie/service/TicketServiceImplTest.java new file mode 100644 index 0000000..dac238b --- /dev/null +++ b/server/src/test/java/com/ticketing/server/movie/service/TicketServiceImplTest.java @@ -0,0 +1,65 @@ +package com.ticketing.server.movie.service; + +import static com.ticketing.server.movie.domain.TicketTest.setupTickets; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.Mockito.when; + +import com.ticketing.server.global.exception.ErrorCode; +import com.ticketing.server.global.exception.TicketingException; +import com.ticketing.server.movie.domain.Ticket; +import com.ticketing.server.movie.domain.repository.TicketRepository; +import com.ticketing.server.movie.service.dto.TicketDetailsDTO; +import com.ticketing.server.payment.application.response.TicketDetailDTO; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class TicketServiceImplTest { + + @Mock + TicketRepository ticketRepository; + + @InjectMocks + TicketServiceImpl ticketService; + + @Test + @DisplayName("paymentId 로 Ticket 목록 조회 실패 - paymentId 가 없는 경우") + void findTicketsByPaymentIdFail() { + // given + when(ticketRepository.findTicketFetchJoinByPaymentId(1L)).thenReturn(Collections.emptyList()); + + // when + // then + assertThatThrownBy(() -> ticketService.findTicketsByPaymentId(1L)) + .isInstanceOf(TicketingException.class) + .extracting("errorCode") + .isEqualTo(ErrorCode.PAYMENT_ID_NOT_FOUND); + } + + @Test + @DisplayName("paymentId 로 Ticket 목록 조회 성공") + void findTicketsByPaymentIdSuccess() { + // given + List tickets = setupTickets(); + when(ticketRepository.findTicketFetchJoinByPaymentId(1L)).thenReturn(List.of(tickets.get(0))); + + // when + TicketDetailsDTO ticketDetailDto = ticketService.findTicketsByPaymentId(1L); + List ticketDetails = ticketDetailDto.getTicketDetails(); + + // then + assertAll( + () -> assertThat(ticketDetails).hasSize(1), + () -> assertThat(ticketDetails.get(0).getPrice()).isEqualTo(15_000) + ); + } + +} diff --git a/server/src/test/java/com/ticketing/server/payment/domain/PaymentTest.java b/server/src/test/java/com/ticketing/server/payment/domain/PaymentTest.java new file mode 100644 index 0000000..87f4731 --- /dev/null +++ b/server/src/test/java/com/ticketing/server/payment/domain/PaymentTest.java @@ -0,0 +1,8 @@ +package com.ticketing.server.payment.domain; + +public class PaymentTest { + + public static final Payment PAYMENT_USER_1 + = new Payment(1L, 111L, "탑건: 매버릭", PaymentType.KAKAO_PAY, PaymentStatus.COMPLETED, "2022-0710-4142", 30_000); + +} diff --git a/server/src/test/java/com/ticketing/server/payment/service/PaymentApisServiceImplTest.java b/server/src/test/java/com/ticketing/server/payment/service/PaymentApisServiceImplTest.java new file mode 100644 index 0000000..16db744 --- /dev/null +++ b/server/src/test/java/com/ticketing/server/payment/service/PaymentApisServiceImplTest.java @@ -0,0 +1,119 @@ +package com.ticketing.server.payment.service; + +import static com.ticketing.server.movie.domain.TicketTest.setupTickets; +import static com.ticketing.server.payment.domain.PaymentTest.PAYMENT_USER_1; +import static com.ticketing.server.user.domain.UserTest.provideCorrectUsers; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.Mockito.when; + +import com.ticketing.server.global.exception.ErrorCode; +import com.ticketing.server.global.exception.TicketingException; +import com.ticketing.server.movie.application.response.TicketDetailsResponse; +import com.ticketing.server.movie.domain.Ticket; +import com.ticketing.server.payment.api.MovieClient; +import com.ticketing.server.payment.api.UserClient; +import com.ticketing.server.payment.api.dto.response.UserDetailResponse; +import com.ticketing.server.payment.application.response.PaymentDetailResponse; +import com.ticketing.server.payment.application.response.TicketDetailDTO; +import com.ticketing.server.payment.domain.repository.PaymentRepository; +import com.ticketing.server.user.domain.User; +import com.ticketing.server.user.service.dto.UserDetailDTO; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class PaymentApisServiceImplTest { + + private Map users; + private List tickets; + + @Mock + UserClient userClient; + + @Mock + MovieClient movieClient; + + @Mock + PaymentRepository paymentRepository; + + + @InjectMocks + PaymentApisServiceImpl paymentApisService; + + @BeforeEach + void init() { + users = provideCorrectUsers().collect(Collectors.toMap(User::getEmail, user -> user)); + tickets = setupTickets(); + } + + @Test + @DisplayName("상세 결제정보 조회 실패 - paymentId 가 존재하지 않을 경우") + void findPaymentDetailNotPaymentIdFail() { + // given + when(paymentRepository.findById(1L)).thenReturn(Optional.empty()); + + // when + // then + assertThatThrownBy(() -> paymentApisService.findPaymentDetail(1L)) + .isInstanceOf(TicketingException.class) + .extracting("errorCode") + .isEqualTo(ErrorCode.PAYMENT_ID_NOT_FOUND); + } + + @Test + @DisplayName("상세 결제정보 조회 실패 - 결제UserId와 JWT 로그인 userId 불일치") + void findPaymentDetailUserIdFail() { + // given + when(paymentRepository.findById(1L)).thenReturn(Optional.of(PAYMENT_USER_1)); + + UserDetailDTO userDetail = new UserDetailDTO(users.get("ticketing2@gmail.com")); + when(userClient.detail()).thenReturn(new UserDetailResponse(userDetail)); + + // when + // then + assertThatThrownBy(() -> paymentApisService.findPaymentDetail(1L)) + .isInstanceOf(TicketingException.class) + .extracting("errorCode") + .isEqualTo(ErrorCode.VALID_USER_ID); + } + + @Test + @DisplayName("상세 결제정보 조회 성공") + void findPaymentDetailSuccess() { + // given + UserDetailDTO userDetail = new UserDetailDTO(users.get("ticketing1@gmail.com")); + TicketDetailsResponse response = new TicketDetailsResponse(Arrays.asList( + new TicketDetailDTO(tickets.get(0)), + new TicketDetailDTO(tickets.get(1)) + )); + + when(paymentRepository.findById(1L)).thenReturn(Optional.of(PAYMENT_USER_1)); + when(userClient.detail()).thenReturn(new UserDetailResponse(userDetail)); + when(movieClient.getTicketsByPaymentId(1L)).thenReturn(response); + + // when + PaymentDetailResponse paymentDetail = paymentApisService.findPaymentDetail(1L); + + // then + assertAll( + () -> assertThat(paymentDetail.getPaymentId()).isEqualTo(1L), + () -> assertThat(paymentDetail.getMovieTitle()).isEqualTo("탑건: 매버릭"), + () -> assertThat(paymentDetail.getPaymentNumber()).isEqualTo("2022-0710-4142"), + () -> assertThat(paymentDetail.getTotalPrice()).isEqualTo(30_000), + () -> assertThat(paymentDetail.getTickets()).hasSize(2) + ); + } + +} diff --git a/server/src/test/java/com/ticketing/server/payment/service/PaymentServiceImplTest.java b/server/src/test/java/com/ticketing/server/payment/service/PaymentServiceImplTest.java index ff60a7e..f0a5791 100644 --- a/server/src/test/java/com/ticketing/server/payment/service/PaymentServiceImplTest.java +++ b/server/src/test/java/com/ticketing/server/payment/service/PaymentServiceImplTest.java @@ -3,13 +3,12 @@ package com.ticketing.server.payment.service; import static com.ticketing.server.payment.domain.PaymentStatus.COMPLETED; import static com.ticketing.server.payment.domain.PaymentType.KAKAO_PAY; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.Mockito.when; import com.ticketing.server.payment.domain.Payment; import com.ticketing.server.payment.domain.repository.PaymentRepository; -import com.ticketing.server.payment.service.dto.CreatePaymentDto; -import com.ticketing.server.user.api.dto.response.SimplePaymentsResponse; +import com.ticketing.server.payment.service.dto.CreatePaymentDTO; +import com.ticketing.server.payment.service.dto.SimplePaymentsDTO; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -28,44 +27,39 @@ class PaymentServiceImplTest { @Mock PaymentRepository paymentRepository; + @InjectMocks PaymentServiceImpl paymentService; @Test - @DisplayName("유저 ID로 결제내역이 없을 경우") + @DisplayName("유저 대체키로 결제내역이 없을 경우") void findSimplePaymentsZero() { // given - when(paymentRepository.findByUserId(2L)).thenReturn(Collections.emptyList()); + when(paymentRepository.findByUserAlternateId(2L)).thenReturn(Collections.emptyList()); // when - SimplePaymentsResponse simplePayments = paymentService.findSimplePayments(2L); + SimplePaymentsDTO simplePayments = paymentService.findSimplePayments(2L); // then - assertAll( - () -> assertThat(simplePayments.getUserId()).isEqualTo(2L) - , () -> assertThat(simplePayments.getPayments()).isEmpty() - ); + assertThat(simplePayments.getPayments()).isEmpty(); } @ParameterizedTest - @DisplayName("유저 ID로 간소 결제내역 조회 성공") + @DisplayName("유저 대체키로 간소 결제내역 조회 성공") @ValueSource(longs = {1L, 2L, 100L, 154L}) - void findSimplePaymentsSuccess(Long userId) { + void findSimplePaymentsSuccess(Long userAlternateId) { // given List payments = Arrays.asList( - Payment.from(CreatePaymentDto.of(userId, "범죄도시2", KAKAO_PAY, COMPLETED, "004-323-77542", 15_000)), - Payment.from(CreatePaymentDto.of(userId, "토르", KAKAO_PAY, COMPLETED, "004-323-77544", 30_000)) + new CreatePaymentDTO(userAlternateId, "범죄도시2", KAKAO_PAY, COMPLETED, "004-323-77542", 15_000).toEntity(), + new CreatePaymentDTO(userAlternateId, "토르", KAKAO_PAY, COMPLETED, "004-323-77544", 30_000).toEntity() ); - when(paymentRepository.findByUserId(userId)).thenReturn(payments); + when(paymentRepository.findByUserAlternateId(userAlternateId)).thenReturn(payments); // when - SimplePaymentsResponse simplePayments = paymentService.findSimplePayments(userId); + SimplePaymentsDTO simplePayments = paymentService.findSimplePayments(userAlternateId); // then - assertAll( - () -> assertThat(simplePayments.getUserId()).isEqualTo(userId) - , () -> assertThat(simplePayments.getPayments()).hasSize(2) - ); + assertThat(simplePayments.getPayments()).hasSize(2); } } diff --git a/server/src/test/java/com/ticketing/server/user/domain/SnowflakeTest.java b/server/src/test/java/com/ticketing/server/user/domain/SnowflakeTest.java new file mode 100644 index 0000000..463faa9 --- /dev/null +++ b/server/src/test/java/com/ticketing/server/user/domain/SnowflakeTest.java @@ -0,0 +1,87 @@ +package com.ticketing.server.user.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.time.Instant; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.Collectors; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class SnowflakeTest { + + @Test + @DisplayName("비트 계산이 정상적으로 처리되었는지 확인") + void nextId_shouldGenerateIdWithCorrectBitsFilled() { + // given + int nodeId = 784; + int sequence = 0; + Snowflake snowflake = new Snowflake(); + long beforeTimestamp = Instant.now().toEpochMilli(); + + // when + long id = snowflake.generateId(); + + // then + long[] attrs = snowflake.parse(id); + assertAll( + () -> assertThat(attrs[0] >= beforeTimestamp).isTrue(), + () -> assertThat(sequence).isEqualTo(attrs[2]) + ); + } + + @Test + @DisplayName("50_000 번 돌렸을 시 유니크 ID 인지 확인") + void nextId_shouldGenerateUniqueId() { + // given + Set ids = new HashSet<>(); + Snowflake snowflake = new Snowflake(); + int iterations = 50_000; + + // when + for (int i = 0; i < iterations; i++) { + ids.add(snowflake.generateId()); + } + + // then + assertThat(ids).hasSize(iterations); + } + + @Test + @DisplayName("멀티스레드 환경 유니크 ID 테스트") + void nextId_shouldGenerateUniqueIdIfCalledFromMultipleThreads() throws InterruptedException, ExecutionException { + // given + int numThreads = 50; + ExecutorService executorService = Executors.newFixedThreadPool(numThreads); + CountDownLatch latch = new CountDownLatch(numThreads); + + Snowflake snowflake = new Snowflake(); + int iterations = 10_000; + + // when + Future[] futures = new Future[iterations]; + for (int i = 0; i < iterations; i++) { + futures[i] = executorService.submit(() -> { + long id = snowflake.generateId(); + latch.countDown(); + return id; + }); + } + + // then + latch.await(); + + Set> set = Arrays.stream(futures) + .collect(Collectors.toSet()); + assertThat(set).hasSize(iterations); + } + +} diff --git a/server/src/test/java/com/ticketing/server/user/domain/UserTest.java b/server/src/test/java/com/ticketing/server/user/domain/UserTest.java index 9a2aed6..221d691 100644 --- a/server/src/test/java/com/ticketing/server/user/domain/UserTest.java +++ b/server/src/test/java/com/ticketing/server/user/domain/UserTest.java @@ -2,12 +2,11 @@ package com.ticketing.server.user.domain; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; import com.ticketing.server.global.exception.TicketingException; import com.ticketing.server.user.service.dto.ChangePasswordDTO; import com.ticketing.server.user.service.dto.DeleteUserDTO; -import com.ticketing.server.user.service.dto.DeleteUserDtoTest; +import com.ticketing.server.user.service.dto.DeleteUserDTOTest; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -22,7 +21,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -class UserTest { +public class UserTest { private Validator validator; private Map users; @@ -74,17 +73,14 @@ class UserTest { User deletedUser = user.delete(deleteUserDto); // then - assertAll( - () -> assertThat(deletedUser.getDeletedAt()).isNotNull() - , () -> assertThat(deletedUser.isDeleted()).isTrue() - ); + assertThat(deletedUser.getDeletedAt()).isNotNull(); } @Test @DisplayName("입력받은 패스워드와 불일치로 변경 실패") void changePasswordFail() { // given - ChangePasswordDTO changePasswordDto = new ChangePasswordDTO("ticketing1@gmail.com", "1234567", "ticketing1234", DeleteUserDtoTest.CUSTOM_PASSWORD_ENCODER); + ChangePasswordDTO changePasswordDto = new ChangePasswordDTO("ticketing1@gmail.com", "1234567", "ticketing1234", DeleteUserDTOTest.CUSTOM_PASSWORD_ENCODER); User user = users.get(changePasswordDto.getEmail()); // when @@ -97,7 +93,7 @@ class UserTest { @DisplayName("패스워드 변경 성공") void changePasswordSuccess() { // given - ChangePasswordDTO changePasswordDto = new ChangePasswordDTO("ticketing1@gmail.com", "123456", "ticketing1234", DeleteUserDtoTest.CUSTOM_PASSWORD_ENCODER); + ChangePasswordDTO changePasswordDto = new ChangePasswordDTO("ticketing1@gmail.com", "123456", "ticketing1234", DeleteUserDTOTest.CUSTOM_PASSWORD_ENCODER); User user = users.get(changePasswordDto.getEmail()); String oldPassword = user.getPassword(); @@ -172,7 +168,20 @@ class UserTest { @DisplayName("grade null 검증") void gradeNull() { // given - User user = new User("유저1", "email@gmail.com", "testPassword01", null, "010-1234-5678"); + User user = new User(111L, "유저1", "email@gmail.com", "testPassword01", null, "010-1234-5678"); + + // when + Set> constraintViolations = validator.validate(user); + + // then + assertThat(constraintViolations).hasSize(1); + } + + @Test + @DisplayName("alternateId null 검증") + void alternateIdNull() { + // given + User user = new User(null, "유저1", "email@gmail.com", "testPassword01", UserGrade.USER, "010-1234-5678"); // when Set> constraintViolations = validator.validate(user); @@ -207,73 +216,73 @@ class UserTest { public static Stream provideCorrectUsers() { return Stream.of( - new User("유저1", "ticketing1@gmail.com", "123456", UserGrade.GUEST, "010-1234-5678") - , new User("유저2", "ticketing2@gmail.com", "qwe123", UserGrade.GUEST, "010-2234-5678") - , new User("유저3", "ticketing3@gmail.com", "ticketing", UserGrade.STAFF, "010-3234-5678") - , new User("유저4", "ticketing4@gmail.com", "ticketing123456", UserGrade.STAFF, "010-4234-5678") + new User(1L, 111L, "유저1", "ticketing1@gmail.com", "123456", UserGrade.USER, "010-1234-5678") + , new User(2L, 222L, "유저2", "ticketing2@gmail.com", "qwe123", UserGrade.USER, "010-2234-5678") + , new User(3L, 333L, "유저3", "ticketing3@gmail.com", "ticketing", UserGrade.STAFF, "010-3234-5678") + , new User(4L, 444L, "유저4", "ticketing4@gmail.com", "ticketing123456", UserGrade.STAFF, "010-4234-5678") ); } public static Stream provideNullOrEmptyOfName() { return Stream.of( - new User(null, "ticketing1@gmail.com", "123456", UserGrade.GUEST, "010-1234-5678") - , new User("", "ticketing2@gmail.com", "qwe123", UserGrade.GUEST, "010-2234-5678") + new User(111L, null, "ticketing1@gmail.com", "123456", UserGrade.USER, "010-1234-5678") + , new User(111L, "", "ticketing2@gmail.com", "qwe123", UserGrade.USER, "010-2234-5678") ); } public static Stream provideNullOrEmptyOfEmail() { return Stream.of( - new User("유저1", null, "123456", UserGrade.GUEST, "010-1234-5678") - , new User("유저2", "", "qwe123", UserGrade.GUEST, "010-2234-5678") + new User(111L, "유저1", null, "123456", UserGrade.USER, "010-1234-5678") + , new User(111L, "유저2", "", "qwe123", UserGrade.USER, "010-2234-5678") ); } public static Stream provideValidationFailedOfEmail() { return Stream.of( - new User("유저1", "email", "123456", UserGrade.GUEST, "010-1234-5678") - , new User("유저2", "@gmail.com", "qwe123", UserGrade.GUEST, "010-2234-5678") - , new User("유저3", "12Bye#domain.com", "ticketing", UserGrade.STAFF, "010-3234-5678") + new User(111L, "유저1", "email", "123456", UserGrade.USER, "010-1234-5678") + , new User(111L, "유저2", "@gmail.com", "qwe123", UserGrade.USER, "010-2234-5678") + , new User(111L, "유저3", "12Bye#domain.com", "ticketing", UserGrade.STAFF, "010-3234-5678") ); } public static Stream provideNullOrEmptyOfPassword() { return Stream.of( - new User("유저1", "ticketing1@gmail.com", null, UserGrade.GUEST, "010-1234-5678") - , new User("유저2", "ticketing2@gmail.com", "", UserGrade.GUEST, "010-2234-5678") + new User(111L, "유저1", "ticketing1@gmail.com", null, UserGrade.USER, "010-1234-5678") + , new User(111L, "유저2", "ticketing2@gmail.com", "", UserGrade.USER, "010-2234-5678") ); } public static Stream provideNullOrEmptyOfPhone() { return Stream.of( - new User("유저1", "ticketing1@gmail.com", "123456", UserGrade.GUEST, null) - , new User("유저2", "ticketing2@gmail.com", "qwe123", UserGrade.GUEST, "") + new User(111L, "유저1", "ticketing1@gmail.com", "123456", UserGrade.USER, null) + , new User(111L, "유저2", "ticketing2@gmail.com", "qwe123", UserGrade.USER, "") ); } public static Stream provideValidationFailedOfPhone() { return Stream.of( - new User("유저1", "ticketing1@gmail.com", "123456", UserGrade.GUEST, "010-123-1234") - , new User("유저2", "ticketing2@gmail.com", "qwe123", UserGrade.GUEST, "02-0444-4044") - , new User("유저3", "ticketing3@gmail.com", "ticketing", UserGrade.STAFF, "033-7953") - , new User("유저4", "ticketing4@gmail.com", "ticketing123456", UserGrade.STAFF, "033-0455-504") + new User(111L, "유저1", "ticketing1@gmail.com", "123456", UserGrade.USER, "010-123-1234") + , new User(111L, "유저2", "ticketing2@gmail.com", "qwe123", UserGrade.USER, "02-0444-4044") + , new User(111L, "유저3", "ticketing3@gmail.com", "ticketing", UserGrade.STAFF, "033-7953") + , new User(111L, "유저4", "ticketing4@gmail.com", "ticketing123456", UserGrade.STAFF, "033-0455-504") ); } public static Stream provideDifferentPasswordDeleteUsers() { return Stream.of( - new DeleteUserDTO("ticketing1@gmail.com", "1234561", DeleteUserDtoTest.CUSTOM_PASSWORD_ENCODER) - , new DeleteUserDTO("ticketing2@gmail.com", "qwe1231", DeleteUserDtoTest.CUSTOM_PASSWORD_ENCODER) - , new DeleteUserDTO("ticketing3@gmail.com", "ticketing1", DeleteUserDtoTest.CUSTOM_PASSWORD_ENCODER) - , new DeleteUserDTO("ticketing4@gmail.com", "ticketing1234561", DeleteUserDtoTest.CUSTOM_PASSWORD_ENCODER) + new DeleteUserDTO("ticketing1@gmail.com", "1234561", DeleteUserDTOTest.CUSTOM_PASSWORD_ENCODER) + , new DeleteUserDTO("ticketing2@gmail.com", "qwe1231", DeleteUserDTOTest.CUSTOM_PASSWORD_ENCODER) + , new DeleteUserDTO("ticketing3@gmail.com", "ticketing1", DeleteUserDTOTest.CUSTOM_PASSWORD_ENCODER) + , new DeleteUserDTO("ticketing4@gmail.com", "ticketing1234561", DeleteUserDTOTest.CUSTOM_PASSWORD_ENCODER) ); } public static Stream provideDeleteUsers() { return Stream.of( - new DeleteUserDTO("ticketing1@gmail.com", "123456", DeleteUserDtoTest.CUSTOM_PASSWORD_ENCODER) - , new DeleteUserDTO("ticketing2@gmail.com", "qwe123", DeleteUserDtoTest.CUSTOM_PASSWORD_ENCODER) - , new DeleteUserDTO("ticketing3@gmail.com", "ticketing", DeleteUserDtoTest.CUSTOM_PASSWORD_ENCODER) - , new DeleteUserDTO("ticketing4@gmail.com", "ticketing123456", DeleteUserDtoTest.CUSTOM_PASSWORD_ENCODER) + new DeleteUserDTO("ticketing1@gmail.com", "123456", DeleteUserDTOTest.CUSTOM_PASSWORD_ENCODER) + , new DeleteUserDTO("ticketing2@gmail.com", "qwe123", DeleteUserDTOTest.CUSTOM_PASSWORD_ENCODER) + , new DeleteUserDTO("ticketing3@gmail.com", "ticketing", DeleteUserDTOTest.CUSTOM_PASSWORD_ENCODER) + , new DeleteUserDTO("ticketing4@gmail.com", "ticketing123456", DeleteUserDTOTest.CUSTOM_PASSWORD_ENCODER) ); } diff --git a/server/src/test/java/com/ticketing/server/user/service/AuthenticationServiceImplTest.java b/server/src/test/java/com/ticketing/server/user/service/AuthenticationServiceImplTest.java index 8623013..ccd1453 100644 --- a/server/src/test/java/com/ticketing/server/user/service/AuthenticationServiceImplTest.java +++ b/server/src/test/java/com/ticketing/server/user/service/AuthenticationServiceImplTest.java @@ -53,7 +53,7 @@ class AuthenticationServiceImplTest { @BeforeEach void init() { useJwtProvider = new JwtProvider(useJwtProperties); - SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(UserGrade.GUEST.name()); + SimpleGrantedAuthority grantedAuthority = new SimpleGrantedAuthority(UserGrade.USER.name()); authenticationToken = new UsernamePasswordAuthenticationToken("ticketing@gmail.com", "123456", Collections.singleton(grantedAuthority)); } diff --git a/server/src/test/java/com/ticketing/server/user/service/UserApisServiceImplTest.java b/server/src/test/java/com/ticketing/server/user/service/UserApisServiceImplTest.java new file mode 100644 index 0000000..11c48f7 --- /dev/null +++ b/server/src/test/java/com/ticketing/server/user/service/UserApisServiceImplTest.java @@ -0,0 +1,89 @@ +package com.ticketing.server.user.service; + +import static com.ticketing.server.payment.domain.PaymentStatus.COMPLETED; +import static com.ticketing.server.payment.domain.PaymentType.KAKAO_PAY; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import com.ticketing.server.payment.application.response.SimplePaymentsResponse; +import com.ticketing.server.payment.service.dto.CreatePaymentDTO; +import com.ticketing.server.payment.service.dto.SimplePaymentDTO; +import com.ticketing.server.user.api.PaymentClient; +import com.ticketing.server.user.application.response.PaymentsResponse; +import com.ticketing.server.user.domain.User; +import com.ticketing.server.user.domain.UserGrade; +import com.ticketing.server.user.service.interfaces.UserService; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class UserApisServiceImplTest { + + User user; + + @BeforeEach + void init() { + user = new User(111L, "유저", "ticketing@gmail.com", "123456", UserGrade.USER, "010-1234-5678"); + } + + @Mock + UserService userService; + + @Mock + PaymentClient paymentClient; + + @InjectMocks + UserApisServiceImpl userApisService; + + @Test + @DisplayName("회원 결제 목록 조회 시 결제목록이 없을 경우") + void findPaymentsByEmailSuccess_paymentEmpty() { + // given + when(userService.findNotDeletedUserByEmail("ticketing@gmail.com")).thenReturn(user); + when(paymentClient.getPayments(any())).thenReturn(new SimplePaymentsResponse(1L, Collections.emptyList())); + + // when + PaymentsResponse paymentDetails = userApisService.findPaymentsByEmail("ticketing@gmail.com"); + + // then + assertAll( + () -> assertThat(paymentDetails.getEmail()).isEqualTo("ticketing@gmail.com") + , () -> assertThat(paymentDetails.getPayments()).hasSize(0) + ); + } + + @Test + @DisplayName("회원 결제목록 조회") + void findPaymentsByEmailSuccess() { + // given + List payments = Arrays.asList( + new SimplePaymentDTO( + new CreatePaymentDTO(1L, "범죄도시2", KAKAO_PAY, COMPLETED, "004-323-77542", 15_000).toEntity()), + new SimplePaymentDTO( + new CreatePaymentDTO(1L, "토르", KAKAO_PAY, COMPLETED, "004-323-77544", 30_000).toEntity()) + ); + + when(userService.findNotDeletedUserByEmail("ticketing@gmail.com")).thenReturn(user); + when(paymentClient.getPayments(any())).thenReturn(new SimplePaymentsResponse(1L, payments)); + + // when + PaymentsResponse paymentDetails = userApisService.findPaymentsByEmail("ticketing@gmail.com"); + + // then + assertAll( + () -> assertThat(paymentDetails.getEmail()).isEqualTo("ticketing@gmail.com") + , () -> assertThat(paymentDetails.getPayments()).hasSize(2) + ); + } + +} diff --git a/server/src/test/java/com/ticketing/server/user/service/UserServiceImplTest.java b/server/src/test/java/com/ticketing/server/user/service/UserServiceImplTest.java index 38a7397..208777f 100644 --- a/server/src/test/java/com/ticketing/server/user/service/UserServiceImplTest.java +++ b/server/src/test/java/com/ticketing/server/user/service/UserServiceImplTest.java @@ -1,30 +1,22 @@ package com.ticketing.server.user.service; -import static com.ticketing.server.payment.domain.PaymentStatus.COMPLETED; -import static com.ticketing.server.payment.domain.PaymentType.KAKAO_PAY; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; +import com.ticketing.server.global.exception.ErrorCode; import com.ticketing.server.global.exception.TicketingException; -import com.ticketing.server.payment.domain.Payment; -import com.ticketing.server.payment.service.dto.CreatePaymentDto; -import com.ticketing.server.payment.service.dto.SimplePaymentDto; -import com.ticketing.server.user.api.PaymentClient; -import com.ticketing.server.user.api.dto.request.SimplePaymentsRequest; -import com.ticketing.server.user.api.dto.response.SimplePaymentsResponse; -import com.ticketing.server.user.application.response.SimplePaymentDetailsResponse; +import com.ticketing.server.user.domain.SequenceGenerator; import com.ticketing.server.user.domain.User; import com.ticketing.server.user.domain.UserGrade; import com.ticketing.server.user.domain.repository.UserRepository; import com.ticketing.server.user.service.dto.ChangePasswordDTO; import com.ticketing.server.user.service.dto.DeleteUserDTO; -import com.ticketing.server.user.service.dto.DeleteUserDtoTest; +import com.ticketing.server.user.service.dto.DeleteUserDTOTest; import com.ticketing.server.user.service.dto.SignUpDTO; -import java.util.Arrays; -import java.util.List; +import com.ticketing.server.user.service.dto.UserDetailDTO; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -46,7 +38,7 @@ class UserServiceImplTest { UserRepository userRepository; @Mock - PaymentClient paymentClient; + SequenceGenerator sequenceGenerator; @InjectMocks UserServiceImpl userService; @@ -54,9 +46,9 @@ class UserServiceImplTest { @BeforeEach void init() { signUpDto = new SignUpDTO("유저", "ticketing@gmail.com", "123456", "010-1234-5678"); - user = new User("유저", "ticketing@gmail.com", "123456", UserGrade.GUEST, "010-1234-5678"); - deleteUserDto = new DeleteUserDTO("ticketing@gmail.com", "123456", DeleteUserDtoTest.CUSTOM_PASSWORD_ENCODER); - changePasswordDto = new ChangePasswordDTO("ticketing@gmail.com", "123456", "ticketing1234", DeleteUserDtoTest.CUSTOM_PASSWORD_ENCODER); + user = new User(111L, "유저", "ticketing@gmail.com", "123456", UserGrade.USER, "010-1234-5678"); + deleteUserDto = new DeleteUserDTO("ticketing@gmail.com", "123456", DeleteUserDTOTest.CUSTOM_PASSWORD_ENCODER); + changePasswordDto = new ChangePasswordDTO("ticketing@gmail.com", "123456", "ticketing1234", DeleteUserDTOTest.CUSTOM_PASSWORD_ENCODER); } @Test @@ -77,6 +69,7 @@ class UserServiceImplTest { // given when(userRepository.findByEmail("ticketing@gmail.com")).thenReturn(Optional.empty()); when(userRepository.save(any())).thenReturn(user); + when(sequenceGenerator.generateId()).thenReturn(123L); // when User user = userService.register(signUpDto); @@ -89,7 +82,7 @@ class UserServiceImplTest { @DisplayName("회원탈퇴 시 이메일이 존재하지 않을 경우") void deleteFail() { // given - when(userRepository.findByEmailAndIsDeletedFalse("ticketing@gmail.com")).thenReturn(Optional.empty()); + when(userRepository.findByEmailAndDeletedAtNull("ticketing@gmail.com")).thenReturn(Optional.empty()); // when // then @@ -101,23 +94,20 @@ class UserServiceImplTest { @DisplayName("회원탈퇴 성공했을 경우") void deleteSuccess() { // given - when(userRepository.findByEmailAndIsDeletedFalse("ticketing@gmail.com")).thenReturn(Optional.of(user)); + when(userRepository.findByEmailAndDeletedAtNull("ticketing@gmail.com")).thenReturn(Optional.of(user)); // when User user = userService.delete(deleteUserDto); // then - assertAll( - () -> assertThat(user.isDeleted()).isTrue(), - () -> assertThat(user.getDeletedAt()).isNotNull() - ); + assertThat(user.getDeletedAt()).isNotNull(); } @Test @DisplayName("패스워드 변경 시 이메일이 존재하지 않을 경우") void changePasswordFail() { // given - when(userRepository.findByEmailAndIsDeletedFalse("ticketing@gmail.com")).thenReturn(Optional.empty()); + when(userRepository.findByEmailAndDeletedAtNull("ticketing@gmail.com")).thenReturn(Optional.empty()); // when // then @@ -129,7 +119,7 @@ class UserServiceImplTest { @DisplayName("패스워드 변경 성공했을 경우") void changePasswordSuccess() { // given - when(userRepository.findByEmailAndIsDeletedFalse("ticketing@gmail.com")).thenReturn(Optional.of(user)); + when(userRepository.findByEmailAndDeletedAtNull("ticketing@gmail.com")).thenReturn(Optional.of(user)); // when User user = userService.changePassword(changePasswordDto); @@ -139,29 +129,35 @@ class UserServiceImplTest { } @Test - @DisplayName("회원 결제목록 조회") - void findSimplePaymentDetailsSuccess() { - // given - List paymentDtos = Arrays.asList( - SimplePaymentDto.from( - Payment.from( - CreatePaymentDto.of(1L, "범죄도시2", KAKAO_PAY, COMPLETED, "004-323-77542", 15_000))), - SimplePaymentDto.from( - Payment.from( - CreatePaymentDto.of(1L, "토르", KAKAO_PAY, COMPLETED, "004-323-77544", 30_000))) - ); + @DisplayName("email 로 유저 디테일 정보 가져오기 성공했을 경우") + void findDetailByEmailSuccess() { + // given + when(userRepository.findByEmail("ticketing@gmail.com")).thenReturn(Optional.of(user)); - when(userRepository.findByEmailAndIsDeletedFalse("ticketing@gmail.com")).thenReturn(Optional.of(user)); - when(paymentClient.getSimplePayments(any())).thenReturn(SimplePaymentsResponse.from(1L, paymentDtos)); - - // when - SimplePaymentDetailsResponse paymentDetails = userService.findSimplePaymentDetails("ticketing@gmail.com"); + // when + UserDetailDTO userDetail = userService.findDetailByEmail("ticketing@gmail.com"); // then assertAll( - () -> assertThat(paymentDetails.getEmail()).isEqualTo("ticketing@gmail.com") - , () -> assertThat(paymentDetails.getPayments()).hasSize(2) + () -> assertThat(userDetail.getEmail()).isEqualTo("ticketing@gmail.com"), + () -> assertThat(userDetail.getName()).isEqualTo("유저"), + () -> assertThat(userDetail.getPhone()).isEqualTo("010-1234-5678"), + () -> assertThat(userDetail.getGrade()).isEqualTo(UserGrade.USER) ); } + @Test + @DisplayName("email 로 유저 디테일 정보 가져오기 실패했을 경우") + void findDetailByEmailFail() { + // given + when(userRepository.findByEmail("ticketing@gmail.com")).thenReturn(Optional.empty()); + + // when + // then + assertThatThrownBy(() -> userService.findDetailByEmail("ticketing@gmail.com")) + .isInstanceOf(TicketingException.class) + .extracting("errorCode") + .isEqualTo(ErrorCode.EMAIL_NOT_FOUND); + } + } diff --git a/server/src/test/java/com/ticketing/server/user/service/dto/DeleteUserDtoTest.java b/server/src/test/java/com/ticketing/server/user/service/dto/DeleteUserDTOTest.java similarity index 96% rename from server/src/test/java/com/ticketing/server/user/service/dto/DeleteUserDtoTest.java rename to server/src/test/java/com/ticketing/server/user/service/dto/DeleteUserDTOTest.java index 2ca6d83..0e646e7 100644 --- a/server/src/test/java/com/ticketing/server/user/service/dto/DeleteUserDtoTest.java +++ b/server/src/test/java/com/ticketing/server/user/service/dto/DeleteUserDTOTest.java @@ -6,7 +6,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.security.crypto.password.PasswordEncoder; -public class DeleteUserDtoTest { +public class DeleteUserDTOTest { public static PasswordEncoder CUSTOM_PASSWORD_ENCODER = new CustomPasswordEncoder(); diff --git a/server/src/test/java/com/ticketing/server/user/service/dto/SignUpDtoTest.java b/server/src/test/java/com/ticketing/server/user/service/dto/SignUpDTOTest.java similarity index 89% rename from server/src/test/java/com/ticketing/server/user/service/dto/SignUpDtoTest.java rename to server/src/test/java/com/ticketing/server/user/service/dto/SignUpDTOTest.java index c3dd90f..0cd6bc9 100644 --- a/server/src/test/java/com/ticketing/server/user/service/dto/SignUpDtoTest.java +++ b/server/src/test/java/com/ticketing/server/user/service/dto/SignUpDTOTest.java @@ -6,7 +6,7 @@ import com.ticketing.server.user.domain.User; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -class SignUpDtoTest { +class SignUpDTOTest { @Test @DisplayName("toUser 메소드로 User 객체 생성") @@ -15,7 +15,7 @@ class SignUpDtoTest { SignUpDTO signUp = new SignUpDTO("유저1", "ticketing@gmail.com", "123456", "010-1234-5678"); // when - User user = signUp.toUser(); + User user = signUp.toUser(111L); // then assertThat(user).isInstanceOf(User.class);