diff --git a/README.md b/README.md index 1f55a8058..3963b09d5 100644 --- a/README.md +++ b/README.md @@ -1,443 +1,9 @@ # 요구사항 문서 - [x] 결제 기능 추가 - - 사용자가 결제를 해야 예약이 가능하도록 수정 - - 결제 실패시 사용자가 실패 이유를 알 수 있어야 한다. + - 사용자가 결제를 해야 예약이 가능하도록 수정 + - 결제 실패시 사용자가 실패 이유를 알 수 있어야 한다. -# API 명세 - -## 예약 조회 API - -### Request - -> GET /reservations HTTP/1.1 - -### Response - -> HTTP/1.1 200 -> -> Content-Type: application/json - -``` JSON -[ - { - "id": 1, - "name": "브라운", - "date": "2023-08-05", - "time": { - "id": 1, - "startAt": "10:00" - } - "theme" : { - "id": 1, - "name": "이름", - "description": "설명", - "thumbnail": "썸네일" - } - } -] -``` - -## 예약 추가 API - -### Request - -> POST /reservations HTTP/1.1 -> content-type: application/json -> cookie: -> -token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwibmFtZSI6ImFkbWluIiwicm9sZSI6IkFETUlOIn0.cwnHsltFeEtOzMHs2Q5-ItawgvBZ140OyWecppNlLoI -> host: localhost:8080 - -```json -{ - "date": "2024-03-01", - "themeId": 1, - "timeId": 1 -} -``` - -### Response - -> HTTP/1.1 201 -> -> Content-Type: application/json -> Location: /reservations/{id} - -```JSON -{ - "id": 1, - "name": "브라운", - "date": "2023-08-05", - "time": { - "id": 1, - "startAt": "10:00" - }, - "theme": { - "id": 1, - "name": "이름", - "description": "설명", - "thumbnail": "썸네일" - } -} -``` - -## 관리자 예약 추가 API - -### Request - -> POST /admin/reservations HTTP/1.1 -> content-type: application/json -> cookie: -> -token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwibmFtZSI6ImFkbWluIiwicm9sZSI6IkFETUlOIn0.cwnHsltFeEtOzMHs2Q5-ItawgvBZ140OyWecppNlLoI -> host: localhost:8080 - -```json -{ - "date": "2024-03-01", - "themeId": 1, - "timeId": 1, - "memberId": 1 -} -``` - -### Response - -```JSON -{ - "id": 1, - "name": "브라운", - "date": "2023-08-05", - "time": { - "id": 1, - "startAt": "10:00" - }, - "theme": { - "id": 1, - "name": "이름", - "description": "설명", - "thumbnail": "썸네일" - } -} -``` - -## 예약 취소 API - -### Request - -> DELETE /reservations/{reservationId} HTTP/1.1 - -### Response - -> HTTP/1.1 204 - -## 시간 추가 API - -### request - -> POST /admin/times HTTP/1.1 -> content-type: application/json - -```JSON -{ - "startAt": "10:00" -} -``` - -### response - -> HTTP/1.1 201 -> Content-Type: application/json -> Location: /times/{id} - -```JSON -{ - "id": 1, - "startAt": "10:00" -} -``` - -## 시간 조회 API - -### request - -> GET /times HTTP/1.1 - -### response - -> HTTP/1.1 200 -> Content-Type: application/json - -```JSON -[ - { - "id": 1, - "startAt": "10:00" - } -] -``` - -## 시간 삭제 API - -### request - -> DELETE /admin/times/1 HTTP/1.1 - -### response - -> HTTP/1.1 204 - -## 테마 조회 API - -### request - -> GET /themes HTTP/1.1 - -### response - -> HTTP/1.1 200 -> Content-Type: application/json - -```json -[ - { - "id": 1, - "name": "레벨2 탈출", - "description": "우테코 레벨2를 탈출하는 내용입니다.", - "thumbnail": "https://i.pinimg.com/236x/6e/bc/46/6ebc461a94a49f9ea3b8bbe2204145d4.jpg" - } -] -``` - -## 테마 추가 API - -### request - -> POST /admin/themes HTTP/1.1 -> content-type: application/json - -```json -{ - "name": "레벨2 탈출", - "description": "우테코 레벨2를 탈출하는 내용입니다.", - "thumbnail": "https://i.pinimg.com/236x/6e/bc/46/6ebc461a94a49f9ea3b8bbe2204145d4.jpg" -} -``` - -### response - -> HTTP/1.1 201 -> Location: /themes/1 -> Content-Type: application/json - -```json -{ - "id": 1, - "name": "레벨2 탈출", - "description": "우테코 레벨2를 탈출하는 내용입니다.", - "thumbnail": "https://i.pinimg.com/236x/6e/bc/46/6ebc461a94a49f9ea3b8bbe2204145d4.jpg" -} -``` - -## 테마 삭제 API - -### request - -> DELETE /admin/themes/{id} HTTP/1.1 - -### response - -> HTTP/1.1 204 - -## 예약 가능 시간 조회 API - -### Request - -> GET /times?date=${date}&themeId=${themeId} - -### response - -> HTTP/1.1 200 -> Content-Type: application/json - -```json -[ - { - "id": 0, - "startAt": "02:53", - "isBooked": false - } -] -``` - -## 로그인 API - -### Request - -> POST /login HTTP/1.1 -> content-type: application/json -> host: localhost:8080 - -```json -{ - "password": "password", - "email": "admin@email.com" -} -``` - -### Response - -> HTTP/1.1 200 OK -> Content-Type: application/json -> Keep-Alive: timeout=60 -> Set-Cookie: -> -token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwibmFtZSI6ImFkbWluIiwicm9sZSI6IkFETUlOIn0.cwnHsltFeEtOzMHs2Q5-ItawgvBZ140OyWecppNlLoI; -> Path=/; HttpOnly - -## 인증 정보 조회 API - -### Request - -> GET /login/check HTTP/1.1 -> cookie: _ga=GA1.1.48222725.1666268105; _ga_QD3BVX7MKT=GS1.1.1687746261.15.1.1687747186.0.0.0; -> Idea-25a74f9c=3cbc3411-daca-48c1-8201-51bdcdd93164; -> -token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwibmFtZSI6IuyWtOuTnOuvvCIsInJvbGUiOiJBRE1JTiJ9.vcK93ONRQYPFCxT5KleSM6b7cl1FE-neSLKaFyslsZM -> host: localhost:8080 - -### Response - -> HTTP/1.1 200 OK -> Connection: keep-alive -> Content-Type: application/json -> Date: Sun, 03 Mar 2024 19:16:56 GMT -> Keep-Alive: timeout=60 -> Transfer-Encoding: chunked - -```json -{ - "name": "어드민" -} -``` - -## 내 예약 조회 API - -### Request - -> GET /reservations/mine HTTP/1.1 -> cookie: token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwibmFtZSI6IuyWtOuTnOuvvCIsInJvbGUiOiJBRE1JTiJ9.vcK93ONRQYPFCxT5KleSM6b7cl1FE-neSLKaFyslsZM -> host: localhost:8080 - -### Response - -> HTTP/1.1 200 -> Content-Type: application/json - -``` JSON -[ - { - "reservationId": 1, - "theme": "테마1", - "date": "2024-03-01", - "time": "10:00", - "status": "예약" - }, - { - "reservationId": 2, - "theme": "테마2", - "date": "2024-03-01", - "time": "12:00", - "status": "1번째 예약 대기" - }, - { - "reservationId": 3, - "theme": "테마3", - "date": "2024-03-01", - "time": "14:00", - "status": "예약" - } -] -``` - -## 예약 대기 추가 API - -### Request - -> POST /reservations/waiting HTTP/1.1 -> cookie: token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwibmFtZSI6IuyWtOuTnOuvvCIsInJvbGUiOiJBRE1JTiJ9.vcK93ONRQYPFCxT5KleSM6b7cl1FE-neSLKaFyslsZM -> host: localhost:8080 - -```json -{ - "date": "2024-03-01", - "themeId": 1, - "timeId": 1 -} -``` - -### Response - -> HTTP/1.1 204 -> Content-Type: application/json - -``` JSON -{ - "id": 1, - "name": "브라운", - "date": "2024-05-26", - "time": { - "id": 1, - "startAt": "02:23" - }, - "theme": { - "id": 1, - "name": "테마1", - "description": "description_cd6209599833", - "thumbnail": "thumbnail_38689040a3b6" - }, - "priority": 1 -} -``` - -## 예약 대기 취소 API - -### Request - -> DELETE /reservations/waiting/{id} HTTP/1.1 - -### Response - -> HTTP/1.1 204 - -## 관리자 예약 대기 목록 조회 - -### Request - -> GET /admin/reservations/waiting HTTP/1.1 -> cookie: token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwibmFtZSI6IuyWtOuTnOuvvCIsInJvbGUiOiJBRE1JTiJ9.vcK93ONRQYPFCxT5KleSM6b7cl1FE-neSLKaFyslsZM -> host: localhost:8080 - -### Response - -``` JSON -[ - { - "id": 1, - "name": "브라운", - "date": "2024-05-26", - "time": { - "id": 1, - "startAt": "02:23" - }, - "theme": { - "id": 1, - "name": "테마1", - "description": "description_cd6209599833", - "thumbnail": "thumbnail_38689040a3b6" - }, - "priority": 1 - } -] -``` +- [x] 내 예약 페이지에 결제 정보 추가 + - [x] paymentKey + - [x] 결제 금액 diff --git a/build.gradle b/build.gradle index 375005d8c..dd5ba6737 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // JWT 라이브러리. https://github.com/jwtk/jjwt implementation 'io.jsonwebtoken:jjwt:0.12.5' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/src/main/java/roomescape/config/SwaggerConfig.java b/src/main/java/roomescape/config/SwaggerConfig.java new file mode 100644 index 000000000..473655eb0 --- /dev/null +++ b/src/main/java/roomescape/config/SwaggerConfig.java @@ -0,0 +1,13 @@ +package roomescape.config; + +import io.swagger.v3.oas.models.OpenAPI; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + @Bean + public OpenAPI chatOpenApi() { + return new OpenAPI(); + } +} diff --git a/src/main/java/roomescape/domain/payment/ApproveApiResponse.java b/src/main/java/roomescape/domain/payment/ApproveApiResponse.java index 741b21b17..250d1388b 100644 --- a/src/main/java/roomescape/domain/payment/ApproveApiResponse.java +++ b/src/main/java/roomescape/domain/payment/ApproveApiResponse.java @@ -1,4 +1,4 @@ package roomescape.domain.payment; -record ApproveApiResponse(String orderId, String paymentKey, long amount) { +record ApproveApiResponse(String orderId, String paymentKey, long totalAmount) { } diff --git a/src/main/java/roomescape/domain/payment/Payment.java b/src/main/java/roomescape/domain/payment/Payment.java index 1428d0348..badd2bb1a 100644 --- a/src/main/java/roomescape/domain/payment/Payment.java +++ b/src/main/java/roomescape/domain/payment/Payment.java @@ -6,7 +6,7 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.ManyToOne; -import roomescape.domain.Member; +import java.util.Objects; import roomescape.domain.Reservation; @Entity @@ -17,40 +17,67 @@ public class Payment { private String orderId; private String paymentKey; private long amount; - @ManyToOne(fetch = FetchType.LAZY) - private Member member; + @ManyToOne(fetch = FetchType.LAZY) private Reservation reservation; protected Payment() { } - public Payment(String orderId, String paymentKey, long amount, Member member) { - this(null, orderId, paymentKey, amount, member, null); + public Payment(String orderId, String paymentKey, long amount) { + this(null, orderId, paymentKey, amount, null); } - public Payment(Long id, String orderId, String paymentKey, long amount, Member member, Reservation reservation) { + public Payment(Long id, String orderId, String paymentKey, long amount, Reservation reservation) { this.id = id; this.orderId = orderId; this.paymentKey = paymentKey; this.amount = amount; - this.member = member; this.reservation = reservation; } public Payment(long id, Payment payment) { - this(id, payment.orderId, payment.paymentKey, payment.amount, payment.member, payment.reservation); + this(id, payment.orderId, payment.paymentKey, payment.amount, payment.reservation); } public Long getId() { return id; } - public Member getMember() { - return member; - } - public Reservation getReservation() { return reservation; } + + public String getPaymentKey() { + return paymentKey; + } + + public long getAmount() { + return amount; + } + + public String getOrderId() { + return orderId; + } + + public void updateReservation(Reservation reservation) { + this.reservation = reservation; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Payment payment = (Payment) o; + return Objects.equals(id, payment.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } } diff --git a/src/main/java/roomescape/domain/payment/PaymentClient.java b/src/main/java/roomescape/domain/payment/PaymentClient.java index 688297cea..5b924209d 100644 --- a/src/main/java/roomescape/domain/payment/PaymentClient.java +++ b/src/main/java/roomescape/domain/payment/PaymentClient.java @@ -7,16 +7,14 @@ import org.springframework.boot.web.client.ClientHttpRequestFactories; import org.springframework.boot.web.client.ClientHttpRequestFactorySettings; import org.springframework.http.HttpHeaders; -import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.stereotype.Component; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestClient; -import roomescape.domain.Member; import roomescape.dto.PaymentApproveRequest; @Component public class PaymentClient { - private final static String UNKNOWN_ERROR = "알 수 없는 오류가 발생했습니다."; + private static final String UNKNOWN_ERROR = "알 수 없는 오류가 발생했습니다."; private final String apiUri; private final String approveSecretKey; private final RestClient restClient; @@ -39,15 +37,15 @@ public PaymentClient( this.errorHandler = errorHandler; } - public Payment approve(PaymentApproveRequest paymentApproveRequest, Member member) { + public Payment approve(PaymentApproveRequest paymentApproveRequest) { try { - return callApiRequest(paymentApproveRequest, member); + return callApiRequest(paymentApproveRequest); } catch (ResourceAccessException e) { throw new ApiCallException(UNKNOWN_ERROR); } } - private Payment callApiRequest(PaymentApproveRequest paymentApproveRequest, Member member) { + private Payment callApiRequest(PaymentApproveRequest paymentApproveRequest) { String encryptedKey = Base64.getEncoder().encodeToString(approveSecretKey.getBytes()); ApproveApiResponse response = Optional.ofNullable(restClient.post() .uri(apiUri) @@ -57,6 +55,6 @@ private Payment callApiRequest(PaymentApproveRequest paymentApproveRequest, Memb .onStatus(errorHandler) .body(ApproveApiResponse.class)) .orElseThrow(() -> new ApiCallException(UNKNOWN_ERROR)); - return new Payment(response.orderId(), response.paymentKey(), response.amount(), member); + return new Payment(response.orderId(), response.paymentKey(), response.totalAmount()); } } diff --git a/src/main/java/roomescape/dto/LoginMemberReservationResponse.java b/src/main/java/roomescape/dto/LoginMemberReservationResponse.java index 7de95a73c..c3b51e1e1 100644 --- a/src/main/java/roomescape/dto/LoginMemberReservationResponse.java +++ b/src/main/java/roomescape/dto/LoginMemberReservationResponse.java @@ -7,5 +7,7 @@ public record LoginMemberReservationResponse(long reservationId, String theme, LocalDate date, LocalTime time, - String status) { + String status, + String paymentKey, + Long amount) { } diff --git a/src/main/java/roomescape/repository/JpaPaymentRepository.java b/src/main/java/roomescape/repository/JpaPaymentRepository.java index b4bd6c858..297b0f262 100644 --- a/src/main/java/roomescape/repository/JpaPaymentRepository.java +++ b/src/main/java/roomescape/repository/JpaPaymentRepository.java @@ -1,6 +1,9 @@ package roomescape.repository; +import java.util.List; +import java.util.Optional; import org.springframework.stereotype.Repository; +import roomescape.domain.Reservation; import roomescape.domain.payment.Payment; import roomescape.repository.jpa.JpaPaymentDao; @@ -16,4 +19,24 @@ public JpaPaymentRepository(JpaPaymentDao jpaPaymentDao) { public Payment save(Payment payment) { return jpaPaymentDao.save(payment); } + + @Override + public List findAllByReservationIn(List reservations) { + return jpaPaymentDao.findAllByReservationIn(reservations); + } + + @Override + public Optional findByOrderIdAndPaymentKey(String orderId, String paymentKey) { + return jpaPaymentDao.findByOrderIdAndPaymentKey(orderId, paymentKey); + } + + @Override + public Optional findByReservationId(Long reservationId) { + return jpaPaymentDao.findByReservationId(reservationId); + } + + @Override + public void deleteByReservationId(Long reservationId) { + jpaPaymentDao.deleteByReservationId(reservationId); + } } diff --git a/src/main/java/roomescape/repository/PaymentRepository.java b/src/main/java/roomescape/repository/PaymentRepository.java index d14e01b7e..3ea8a391b 100644 --- a/src/main/java/roomescape/repository/PaymentRepository.java +++ b/src/main/java/roomescape/repository/PaymentRepository.java @@ -1,7 +1,18 @@ package roomescape.repository; +import java.util.List; +import java.util.Optional; +import roomescape.domain.Reservation; import roomescape.domain.payment.Payment; public interface PaymentRepository { Payment save(Payment payment); + + List findAllByReservationIn(List reservations); + + Optional findByOrderIdAndPaymentKey(String orderId, String paymentKey); + + Optional findByReservationId(Long reservationId); + + void deleteByReservationId(Long reservationId); } diff --git a/src/main/java/roomescape/repository/jpa/JpaPaymentDao.java b/src/main/java/roomescape/repository/jpa/JpaPaymentDao.java index c650c3f7c..1737d7cc2 100644 --- a/src/main/java/roomescape/repository/jpa/JpaPaymentDao.java +++ b/src/main/java/roomescape/repository/jpa/JpaPaymentDao.java @@ -1,7 +1,18 @@ package roomescape.repository.jpa; +import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import roomescape.domain.Reservation; import roomescape.domain.payment.Payment; public interface JpaPaymentDao extends JpaRepository { + + public List findAllByReservationIn(List reservations); + + public Optional findByOrderIdAndPaymentKey(String orderId, String paymentKey); + + public Optional findByReservationId(Long reservationId); + + public void deleteByReservationId(Long reservationId); } diff --git a/src/main/java/roomescape/service/PaymentService.java b/src/main/java/roomescape/service/PaymentService.java index 95d99f076..0acd9f422 100644 --- a/src/main/java/roomescape/service/PaymentService.java +++ b/src/main/java/roomescape/service/PaymentService.java @@ -4,7 +4,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import roomescape.domain.Member; import roomescape.domain.payment.Payment; import roomescape.domain.payment.PaymentClient; import roomescape.dto.PaymentApproveRequest; @@ -28,9 +27,10 @@ public PaymentService(PaymentClient paymentClient, PaymentRepository paymentRepo } public void approve(PaymentApproveRequest paymentApproveRequest, long memberId) { - Member paidMember = memberRepository.findById(memberId) - .orElseThrow(() -> new RoomescapeException(NOT_FOUND_MEMBER)); - Payment payment = paymentClient.approve(paymentApproveRequest, paidMember); + if (memberRepository.findById(memberId).isEmpty()) { + throw new RoomescapeException(NOT_FOUND_MEMBER); + } + Payment payment = paymentClient.approve(paymentApproveRequest); paymentRepository.save(payment); } } diff --git a/src/main/java/roomescape/service/ReservationService.java b/src/main/java/roomescape/service/ReservationService.java index 73669a7c6..868445ad4 100644 --- a/src/main/java/roomescape/service/ReservationService.java +++ b/src/main/java/roomescape/service/ReservationService.java @@ -12,11 +12,14 @@ import roomescape.domain.Member; import roomescape.domain.Reservation; import roomescape.domain.ReservationWaiting; +import roomescape.domain.payment.Payment; import roomescape.dto.LoginMemberReservationResponse; +import roomescape.dto.PaymentApproveRequest; import roomescape.dto.ReservationRequest; import roomescape.dto.ReservationResponse; import roomescape.exception.RoomescapeException; import roomescape.repository.MemberRepository; +import roomescape.repository.PaymentRepository; import roomescape.repository.ReservationRepository; import roomescape.repository.ReservationWaitingRepository; import roomescape.service.finder.ReservationFinder; @@ -30,14 +33,17 @@ public class ReservationService { private final ReservationWaitingRepository waitingRepository; private final ReservationFinder reservationFinder; private final MemberRepository memberRepository; + private final PaymentRepository paymentRepository; public ReservationService(ReservationRepository reservationRepository, ReservationWaitingRepository waitingRepository, - ReservationFinder reservationFinder, MemberRepository memberFinder) { + ReservationFinder reservationFinder, MemberRepository memberFinder, + PaymentRepository paymentRepository) { this.reservationRepository = reservationRepository; this.waitingRepository = waitingRepository; this.reservationFinder = reservationFinder; this.memberRepository = memberFinder; + this.paymentRepository = paymentRepository; } public ReservationResponse save(ReservationRequest reservationRequest) { @@ -46,9 +52,21 @@ public ReservationResponse save(ReservationRequest reservationRequest) { beforeSave.validatePastTimeReservation(); Reservation saved = reservationRepository.save(beforeSave); + updatePayment(saved, reservationRequest.approveRequest()); return toResponse(saved); } + private void updatePayment(Reservation saved, PaymentApproveRequest approveRequest) { + if (approveRequest == null) { + return; + } + paymentRepository.findByOrderIdAndPaymentKey(approveRequest.orderId(), approveRequest.paymentKey()) + .ifPresent(payment -> { + payment.updateReservation(saved); + paymentRepository.save(payment); + }); + } + public List findAll() { return reservationRepository.findAll().stream() .map(ReservationResponseMapper::toResponse) @@ -64,9 +82,11 @@ public List findByMemberAndThemeBetweenDates(long memberId, } public List findByMemberId(long memberId) { - return reservationRepository.findByMemberId(memberId) - .stream() - .map(LoginMemberReservationResponseMapper::toResponse) + List reservations = reservationRepository.findByMemberId(memberId); + List payments = paymentRepository.findAllByReservationIn(reservations); + + return reservations.stream() + .map(reservation -> LoginMemberReservationResponseMapper.toResponse(reservation, payments)) .toList(); } @@ -107,6 +127,7 @@ private void updateReservationAndDeleteTopWaiting(Reservation reservation, Reser } private void deleteReservation(long reservationId) { + paymentRepository.deleteByReservationId(reservationId); reservationRepository.delete(reservationId); } } diff --git a/src/main/java/roomescape/service/mapper/LoginMemberReservationResponseMapper.java b/src/main/java/roomescape/service/mapper/LoginMemberReservationResponseMapper.java index 313eb1179..6d33ef28e 100644 --- a/src/main/java/roomescape/service/mapper/LoginMemberReservationResponseMapper.java +++ b/src/main/java/roomescape/service/mapper/LoginMemberReservationResponseMapper.java @@ -1,6 +1,9 @@ package roomescape.service.mapper; +import java.util.List; +import java.util.Optional; import roomescape.domain.Reservation; +import roomescape.domain.payment.Payment; import roomescape.dto.LoginMemberReservationResponse; import roomescape.dto.ReservationWaitingResponse; @@ -11,7 +14,27 @@ public static LoginMemberReservationResponse toResponse(Reservation reservation) reservation.getThemeName(), reservation.getDate(), reservation.getTime(), - "예약"); + "예약", + null, null); + } + + public static LoginMemberReservationResponse toResponse(Reservation reservation, Optional payment) { + return payment.map(value -> new LoginMemberReservationResponse( + reservation.getId(), + reservation.getThemeName(), + reservation.getDate(), + reservation.getTime(), + "예약", + value.getPaymentKey(), + value.getAmount())) + .orElseGet(() -> toResponse(reservation)); + } + + public static LoginMemberReservationResponse toResponse(Reservation reservation, List payments) { + Optional paymentByReservation = payments.stream() + .filter(payment -> payment.getReservation().equals(reservation)) + .findFirst(); + return toResponse(reservation, paymentByReservation); } public static LoginMemberReservationResponse from(ReservationWaitingResponse waitingResponse) { @@ -20,7 +43,8 @@ public static LoginMemberReservationResponse from(ReservationWaitingResponse wai waitingResponse.themeName(), waitingResponse.date(), waitingResponse.startAt(), - "%d번째 예약 대기".formatted(waitingResponse.priority()) + "%d번째 예약 대기".formatted(waitingResponse.priority()), + null, null ); } } diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 05b9cebdb..84882e83b 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,115 +1,65 @@ -- 테마 목록 : 11개 INSERT INTO theme (name, description, thumbnail, CREATE_AT) -VALUES ('테마1', '재밌는 테마입니다', 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES ('테마1', '재밌는 테마입니다', + 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', + DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO theme (name, description, thumbnail, CREATE_AT) -VALUES ('테마2', '재밌는 테마입니다', 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES ('테마2', '재밌는 테마입니다', + 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', + DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO theme (name, description, thumbnail, CREATE_AT) -VALUES ('테마3', '재밌는 테마입니다', 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES ('테마3', '재밌는 테마입니다', + 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', + DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO theme (name, description, thumbnail, CREATE_AT) -VALUES ('테마4', '재밌는 테마입니다', 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO theme (name, description, thumbnail, CREATE_AT) -VALUES ('테마5', '재밌는 테마입니다', 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO theme (name, description, thumbnail, CREATE_AT) -VALUES ('테마6', '재밌는 테마입니다', 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO theme (name, description, thumbnail, CREATE_AT) -VALUES ('테마7', '재밌는 테마입니다', 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO theme (name, description, thumbnail, CREATE_AT) -VALUES ('테마8', '재밌는 테마입니다', 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO theme (name, description, thumbnail, CREATE_AT) -VALUES ('테마9', '재밌는 테마입니다', 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO theme (name, description, thumbnail, CREATE_AT) -VALUES ('테마10', '재밌는 테마입니다', 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO theme (name, description, thumbnail, CREATE_AT) -VALUES ('테마11', '재밌는 테마입니다', 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES ('테마4', '재밌는 테마입니다', + 'https://www.google.co.kr/url?sa=i&url=http%3A%2F%2Fwww.code-k.co.kr%2Fsub%2Fcode_sub03.html%3FR_JIJEM%3DS1&psig=AOvVaw20fNjL28MSMMiR0Nb57Eh-&ust=1714695060162000&source=images&cd=vfe&opi=89978449&ved=0CBIQjRxqFwoTCOiO2oLX7YUDFQAAAAAdAAAAABAE', + DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -- 예약 시간 목록 : 5개 INSERT INTO reservation_time (start_at, CREATE_AT) -VALUES ('08:00', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES ('08:00', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO reservation_time (start_at, CREATE_AT) -VALUES ('10:00', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES ('10:00', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO reservation_time (start_at, CREATE_AT) -VALUES ('13:00', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES ('13:00', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO reservation_time (start_at, CREATE_AT) -VALUES ('21:00', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES ('21:00', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO reservation_time (start_at, CREATE_AT) -VALUES ('23:00', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES ('23:00', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -- 유저 목록 : 2개 INSERT INTO member(name, email, password, role, CREATE_AT) -VALUES ('member', 'email@email.com', 'f6f2ea8f45d8a057c9566a33f99474da2e5c6a6604d736121650e2730c6fb0a3', 'MEMBER', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES ('member', 'email@email.com', 'f6f2ea8f45d8a057c9566a33f99474da2e5c6a6604d736121650e2730c6fb0a3', 'MEMBER', + DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO member(name, email, password, role, CREATE_AT) -VALUES ('admin', 'email2@email.com', 'f6f2ea8f45d8a057c9566a33f99474da2e5c6a6604d736121650e2730c6fb0a3', 'ADMIN', DATEADD('DAY', -3, CURRENT_TIMESTAMP)); - --- 테마 1 예약 목록 : 5개 -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 1, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 2, 1, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 3, 1, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 4, 1, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 5, 1, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); - --- 테마 2 예약 목록 : 4개 -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 2, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 2, 2, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 3, 2, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 4, 2, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); - --- 테마 3 예약 목록 : 2개 -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 3, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 2, 3, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES ('admin', 'email2@email.com', 'f6f2ea8f45d8a057c9566a33f99474da2e5c6a6604d736121650e2730c6fb0a3', 'ADMIN', + DATEADD('DAY', -3, CURRENT_TIMESTAMP)); --- 테마 4 예약 목록 : 5개 +-- 테마 1 예약 목록 : 4개 INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 4, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 1, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 2, 4, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES (DATEADD('DAY', -3, CURRENT_DATE), 2, 1, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 3, 4, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES (DATEADD('DAY', -3, CURRENT_DATE), 3, 1, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 4, 4, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 5, 4, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES (DATEADD('DAY', -3, CURRENT_DATE), 4, 1, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); --- 테마 5 예약 목록 : 2개 +-- 테마 2 예약 목록 : 2개 INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 5, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 2, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 5, 5, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES (DATEADD('DAY', -3, CURRENT_DATE), 2, 2, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); --- 테마 6 예약 목록 : 3개 -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 6, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +-- 테마 3 예약 목록 : 1개 INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 2, 6, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 3, 6, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); - --- 테마 7 예약 목록 -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 7, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 3, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); --- 테마 8 예약 목록 +-- 테마 4 예약 목록 : 3개 INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 8, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); - --- 테마 9 예약 목록 +VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 4, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 9, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); - --- 테마 10 예약 목록 -INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 1, 10, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); - --- 테마 11 예약 목록 +VALUES (DATEADD('DAY', -3, CURRENT_DATE), 2, 4, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); INSERT INTO reservation (date, time_id, theme_id, reservation_member_id, CREATE_AT) -VALUES (DATEADD('DAY', -3, CURRENT_DATE), 5, 11, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); +VALUES (DATEADD('DAY', -3, CURRENT_DATE), 3, 4, 1, DATEADD('DAY', -3, CURRENT_TIMESTAMP)); diff --git a/src/main/resources/static/js/reservation-mine.js b/src/main/resources/static/js/reservation-mine.js index b0cf9b681..1d9ddd382 100644 --- a/src/main/resources/static/js/reservation-mine.js +++ b/src/main/resources/static/js/reservation-mine.js @@ -34,7 +34,15 @@ function render(data) { requestDeleteWaiting(item.reservationId).then(() => window.location.reload()); }; } else { // 예약 완료 상태일 때 + console.log(item.amount); + row.insertCell(5).textContent = item.paymentKey; + row.insertCell(6).textContent = item.amount; cancelButton.onclick = function () { + /* + TODO: [미션4 - 2단계] 내 예약 목록 조회 시, + 예약 완료 상태일 때 결제 정보를 함께 보여주기 + 결제 정보 필드명은 자신의 response 에 맞게 변경하기 + */ requestDeleteReservation(item.reservationId).then(() => window.location.reload()); }; } diff --git a/src/main/resources/static/js/user-reservation.js b/src/main/resources/static/js/user-reservation.js index cb959cc6a..24826b897 100644 --- a/src/main/resources/static/js/user-reservation.js +++ b/src/main/resources/static/js/user-reservation.js @@ -1,265 +1,267 @@ const THEME_API_ENDPOINT = '/themes'; document.addEventListener('DOMContentLoaded', () => { - requestRead(THEME_API_ENDPOINT) - .then(renderTheme) - .catch(error => console.error('Error fetching times:', error)); + requestRead(THEME_API_ENDPOINT) + .then(renderTheme) + .catch(error => console.error('Error fetching times:', error)); - flatpickr("#datepicker", { - inline: true, - onChange: function (selectedDates, dateStr, instance) { - if (dateStr === '') return; - checkDate(); - } - }); + flatpickr("#datepicker", { + inline: true, + onChange: function (selectedDates, dateStr, instance) { + if (dateStr === '') return; + checkDate(); + } + }); - // ------ 결제위젯 초기화 ------ - // @docs https://docs.tosspayments.com/reference/widget-sdk#sdk-설치-및-초기화 - // @docs https://docs.tosspayments.com/reference/widget-sdk#renderpaymentmethods선택자-결제-금액-옵션 - const paymentAmount = 50; - const widgetClientKey = "test_gck_docs_Ovk5rk1EwkEbP0W43n07xlzm"; - const paymentWidget = PaymentWidget(widgetClientKey, PaymentWidget.ANONYMOUS); - paymentWidget.renderPaymentMethods( - "#payment-method", - {value: paymentAmount}, - {variantKey: "DEFAULT"} - ); + // ------ 결제위젯 초기화 ------ + // @docs https://docs.tosspayments.com/reference/widget-sdk#sdk-설치-및-초기화 + // @docs https://docs.tosspayments.com/reference/widget-sdk#renderpaymentmethods선택자-결제-금액-옵션 + const paymentAmount = 1270000; + const widgetClientKey = "test_gck_docs_Ovk5rk1EwkEbP0W43n07xlzm"; + const paymentWidget = PaymentWidget(widgetClientKey, PaymentWidget.ANONYMOUS); + paymentWidget.renderPaymentMethods( + "#payment-method", + {value: paymentAmount}, + {variantKey: "DEFAULT"} + ); - document.getElementById('theme-slots').addEventListener('click', event => { - if (event.target.classList.contains('theme-slot')) { - document.querySelectorAll('.theme-slot').forEach(slot => slot.classList.remove('active')); - event.target.classList.add('active'); - checkDateAndTheme(); - } - }); + document.getElementById('theme-slots').addEventListener('click', event => { + if (event.target.classList.contains('theme-slot')) { + document.querySelectorAll('.theme-slot').forEach(slot => slot.classList.remove('active')); + event.target.classList.add('active'); + checkDateAndTheme(); + } + }); - document.getElementById('time-slots').addEventListener('click', event => { - if (event.target.classList.contains('time-slot') && !event.target.classList.contains('disabled')) { - document.querySelectorAll('.time-slot').forEach(slot => slot.classList.remove('active')); - event.target.classList.add('active'); - checkDateAndThemeAndTime(); - } - }); + document.getElementById('time-slots').addEventListener('click', event => { + if (event.target.classList.contains('time-slot') && !event.target.classList.contains('disabled')) { + document.querySelectorAll('.time-slot').forEach(slot => slot.classList.remove('active')); + event.target.classList.add('active'); + checkDateAndThemeAndTime(); + } + }); - document.getElementById('reserve-button').addEventListener('click', onReservationButtonClickWithPaymentWidget); - document.getElementById('wait-button').addEventListener('click', onWaitButtonClick); - function onReservationButtonClickWithPaymentWidget(event) { - onReservationButtonClick(event, paymentWidget); - } + document.getElementById('reserve-button').addEventListener('click', onReservationButtonClickWithPaymentWidget); + document.getElementById('wait-button').addEventListener('click', onWaitButtonClick); + + function onReservationButtonClickWithPaymentWidget(event) { + onReservationButtonClick(event, paymentWidget); + } }); function renderTheme(themes) { - const themeSlots = document.getElementById('theme-slots'); - themeSlots.innerHTML = ''; - themes.forEach(theme => { - const name = theme.name; - const themeId = theme.id; - themeSlots.appendChild(createSlot('theme', name, themeId)); - }); + const themeSlots = document.getElementById('theme-slots'); + themeSlots.innerHTML = ''; + themes.forEach(theme => { + const name = theme.name; + const themeId = theme.id; + themeSlots.appendChild(createSlot('theme', name, themeId)); + }); } function createSlot(type, text, id, booked) { - const div = document.createElement('div'); - div.className = type + '-slot cursor-pointer bg-light border rounded p-3 mb-2'; - div.textContent = text; - div.setAttribute('data-' + type + '-id', id); - if (type === 'time') { - div.setAttribute('data-time-booked', booked); - } - return div; + const div = document.createElement('div'); + div.className = type + '-slot cursor-pointer bg-light border rounded p-3 mb-2'; + div.textContent = text; + div.setAttribute('data-' + type + '-id', id); + if (type === 'time') { + div.setAttribute('data-time-booked', booked); + } + return div; } function checkDate() { - const selectedDate = document.getElementById("datepicker").value; - if (selectedDate) { - const themeSection = document.getElementById("theme-section"); - if (themeSection.classList.contains("disabled")) { - themeSection.classList.remove("disabled"); - } - const timeSlots = document.getElementById('time-slots'); - timeSlots.innerHTML = ''; + const selectedDate = document.getElementById("datepicker").value; + if (selectedDate) { + const themeSection = document.getElementById("theme-section"); + if (themeSection.classList.contains("disabled")) { + themeSection.classList.remove("disabled"); + } + const timeSlots = document.getElementById('time-slots'); + timeSlots.innerHTML = ''; - requestRead(THEME_API_ENDPOINT) - .then(renderTheme) - .catch(error => console.error('Error fetching times:', error)); - } + requestRead(THEME_API_ENDPOINT) + .then(renderTheme) + .catch(error => console.error('Error fetching times:', error)); + } } function checkDateAndTheme() { - const selectedDate = document.getElementById("datepicker").value; - const selectedThemeElement = document.querySelector('.theme-slot.active'); - if (selectedDate && selectedThemeElement) { - const selectedThemeId = selectedThemeElement.getAttribute('data-theme-id'); - fetchAvailableTimes(selectedDate, selectedThemeId); - } + const selectedDate = document.getElementById("datepicker").value; + const selectedThemeElement = document.querySelector('.theme-slot.active'); + if (selectedDate && selectedThemeElement) { + const selectedThemeId = selectedThemeElement.getAttribute('data-theme-id'); + fetchAvailableTimes(selectedDate, selectedThemeId); + } } function fetchAvailableTimes(date, themeId) { - fetch(`/times?date=${date}&themeId=${themeId}`, { // 예약 가능 시간 조회 API endpoint - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }).then(response => { - if (response.status === 200) return response.json(); - throw new Error('Read failed'); - }).then(renderAvailableTimes) - .catch(error => console.error("Error fetching available times:", error)); + fetch(`/times?date=${date}&themeId=${themeId}`, { // 예약 가능 시간 조회 API endpoint + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }).then(response => { + if (response.status === 200) return response.json(); + throw new Error('Read failed'); + }).then(renderAvailableTimes) + .catch(error => console.error("Error fetching available times:", error)); } function renderAvailableTimes(times) { - const timeSection = document.getElementById("time-section"); - if (timeSection.classList.contains("disabled")) { - timeSection.classList.remove("disabled"); - } + const timeSection = document.getElementById("time-section"); + if (timeSection.classList.contains("disabled")) { + timeSection.classList.remove("disabled"); + } - const timeSlots = document.getElementById('time-slots'); - timeSlots.innerHTML = ''; - if (times.length === 0) { - timeSlots.innerHTML = '
선택할 수 있는 시간이 없습니다.
'; - return; - } - times.forEach(time => { - const startAt = time.startAt; - const timeId = time.id; - const alreadyBooked = time.isBooked; + const timeSlots = document.getElementById('time-slots'); + timeSlots.innerHTML = ''; + if (times.length === 0) { + timeSlots.innerHTML = '
선택할 수 있는 시간이 없습니다.
'; + return; + } + times.forEach(time => { + const startAt = time.startAt; + const timeId = time.id; + const alreadyBooked = time.isBooked; - const div = createSlot('time', startAt, timeId, alreadyBooked); // createSlot('time', 시작 시간, time id, 예약 여부) - timeSlots.appendChild(div); - }); + const div = createSlot('time', startAt, timeId, alreadyBooked); // createSlot('time', 시작 시간, time id, 예약 여부) + timeSlots.appendChild(div); + }); } function checkDateAndThemeAndTime() { - const selectedDate = document.getElementById("datepicker").value; - const selectedThemeElement = document.querySelector('.theme-slot.active'); - const selectedTimeElement = document.querySelector('.time-slot.active'); - const reserveButton = document.getElementById("reserve-button"); - const waitButton = document.getElementById("wait-button"); + const selectedDate = document.getElementById("datepicker").value; + const selectedThemeElement = document.querySelector('.theme-slot.active'); + const selectedTimeElement = document.querySelector('.time-slot.active'); + const reserveButton = document.getElementById("reserve-button"); + const waitButton = document.getElementById("wait-button"); - if (selectedDate && selectedThemeElement && selectedTimeElement) { - if (selectedTimeElement.getAttribute('data-time-booked') === 'true') { - // 선택된 시간이 이미 예약된 경우 - reserveButton.classList.add("disabled"); - waitButton.classList.remove("disabled"); // 예약 대기 버튼 활성화 + if (selectedDate && selectedThemeElement && selectedTimeElement) { + if (selectedTimeElement.getAttribute('data-time-booked') === 'true') { + // 선택된 시간이 이미 예약된 경우 + reserveButton.classList.add("disabled"); + waitButton.classList.remove("disabled"); // 예약 대기 버튼 활성화 + } else { + // 선택된 시간이 예약 가능한 경우 + reserveButton.classList.remove("disabled"); + waitButton.classList.add("disabled"); // 예약 대기 버튼 비활성화 + } } else { - // 선택된 시간이 예약 가능한 경우 - reserveButton.classList.remove("disabled"); - waitButton.classList.add("disabled"); // 예약 대기 버튼 비활성화 + // 날짜, 테마, 시간 중 하나라도 선택되지 않은 경우 + reserveButton.classList.add("disabled"); + waitButton.classList.add("disabled"); } - } else { - // 날짜, 테마, 시간 중 하나라도 선택되지 않은 경우 - reserveButton.classList.add("disabled"); - waitButton.classList.add("disabled"); - } } function onReservationButtonClick(event, paymentWidget) { - const selectedDate = document.getElementById("datepicker").value; - const selectedThemeId = document.querySelector('.theme-slot.active')?.getAttribute('data-theme-id'); - const selectedThemeName = document.querySelector('.theme-slot.active')?.innerHTML; - const selectedTimeId = document.querySelector('.time-slot.active')?.getAttribute('data-time-id'); - if (selectedDate && selectedThemeId && selectedTimeId) { - const reservationData = { - date: selectedDate, - themeId: Number(selectedThemeId), - timeId: Number(selectedTimeId) - }; - const generateRandomString = () => - window.btoa(Math.random()).slice(0, 20); - // TOSS 결제 위젯 Javascript SDK 연동 방식 중 'Promise로 처리하기'를 적용함 - // https://docs.tosspayments.com/reference/widget-sdk#promise%EB%A1%9C-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0 - const orderIdPrefix = "MyNameIsNicoRobin"; - paymentWidget.requestPayment({ - orderId: orderIdPrefix + generateRandomString(), - orderName: selectedThemeName +" 테마 예약 결제" - }).then(function (data) { - console.debug(data); - fetchReservationPayment(data, reservationData); - }).catch(function (error) { - console.dir(error); - alert(error.code + " :" + error.message + "/ orderId : " + err.orderId); - }); + const selectedDate = document.getElementById("datepicker").value; + const selectedThemeId = document.querySelector('.theme-slot.active')?.getAttribute('data-theme-id'); + const selectedThemeName = document.querySelector('.theme-slot.active')?.innerHTML; + const selectedTimeId = document.querySelector('.time-slot.active')?.getAttribute('data-time-id'); + if (selectedDate && selectedThemeId && selectedTimeId) { + const reservationData = { + date: selectedDate, + themeId: Number(selectedThemeId), + timeId: Number(selectedTimeId) + }; + const generateRandomString = () => + window.btoa(Math.random()).slice(0, 20); + // TOSS 결제 위젯 Javascript SDK 연동 방식 중 'Promise로 처리하기'를 적용함 + // https://docs.tosspayments.com/reference/widget-sdk#promise%EB%A1%9C-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0 + const orderIdPrefix = "MyNameIsNicoRobin"; + paymentWidget.requestPayment({ + orderId: orderIdPrefix + generateRandomString(), + orderName: selectedThemeName + " 테마 예약 결제" + }).then(function (data) { + console.debug(data); + fetchReservationPayment(data, reservationData); + }).catch(function (error) { + console.dir(error); + alert(error.code + " :" + error.message + "/ orderId : " + err.orderId); + }); - } else { - alert("Please select a date, theme, and time before making a reservation."); - } + } else { + alert("Please select a date, theme, and time before making a reservation."); + } } + async function fetchReservationPayment(paymentData, reservationData) { - const reservationPaymentRequest = { - date: reservationData.date, - themeId: reservationData.themeId, - timeId: reservationData.timeId, - approveRequest : { - paymentKey: paymentData.paymentKey, - orderId: paymentData.orderId, - amount: paymentData.amount + const reservationPaymentRequest = { + date: reservationData.date, + themeId: reservationData.themeId, + timeId: reservationData.timeId, + approveRequest: { + paymentKey: paymentData.paymentKey, + orderId: paymentData.orderId, + amount: paymentData.amount + } } - } - const reservationURL = "/reservations"; - fetch(reservationURL, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(reservationPaymentRequest), - }).then(response => { - if (!response.ok) { - return response.json().then(errorBody => { - console.error("예약 결제 실패 : " + errorBody.message); - window.alert(errorBody.message); - }); - } else { - response.json().then(successBody => { - console.log("예약 결제 성공 : " + JSON.stringify(successBody)); - window.location.reload(); - }); - } - }).catch(error => { - console.error(error.message); - }); + const reservationURL = "/reservations"; + fetch(reservationURL, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(reservationPaymentRequest), + }).then(response => { + if (!response.ok) { + return response.json().then(errorBody => { + console.error("예약 결제 실패 : " + errorBody.message); + window.alert(errorBody.message); + }); + } else { + response.json().then(successBody => { + console.log("예약 결제 성공 : " + JSON.stringify(successBody)); + window.location.reload(); + }); + } + }).catch(error => { + console.error(error.message); + }); } function onWaitButtonClick() { - const selectedDate = document.getElementById("datepicker").value; - const selectedThemeId = document.querySelector('.theme-slot.active')?.getAttribute('data-theme-id'); - const selectedTimeId = document.querySelector('.time-slot.active')?.getAttribute('data-time-id'); + const selectedDate = document.getElementById("datepicker").value; + const selectedThemeId = document.querySelector('.theme-slot.active')?.getAttribute('data-theme-id'); + const selectedTimeId = document.querySelector('.time-slot.active')?.getAttribute('data-time-id'); - if (selectedDate && selectedThemeId && selectedTimeId) { - const reservationData = { - date: selectedDate, - themeId: Number(selectedThemeId), - timeId: Number(selectedTimeId) - }; + if (selectedDate && selectedThemeId && selectedTimeId) { + const reservationData = { + date: selectedDate, + themeId: Number(selectedThemeId), + timeId: Number(selectedTimeId) + }; - fetch('/reservations/waiting', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(reservationData) - }) - .then(response => { - if (!response.ok) throw new Error('Reservation waiting failed'); - return response.json(); + fetch('/reservations/waiting', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(reservationData) }) - .then(data => { - alert('Reservation waiting successful!'); - window.location.href = "/"; - }) - .catch(error => { - alert("An error occurred while making the reservation waiting."); - console.error(error); - }); - } else { - alert("Please select a date, theme, and time before making a reservation waiting."); - } + .then(response => { + if (!response.ok) throw new Error('Reservation waiting failed'); + return response.json(); + }) + .then(data => { + alert('Reservation waiting successful!'); + window.location.href = "/"; + }) + .catch(error => { + alert("An error occurred while making the reservation waiting."); + console.error(error); + }); + } else { + alert("Please select a date, theme, and time before making a reservation waiting."); + } } function requestRead(endpoint) { - return fetch(endpoint) - .then(response => { - if (response.status === 200) return response.json(); - throw new Error('Read failed'); - }); + return fetch(endpoint) + .then(response => { + if (response.status === 200) return response.json(); + throw new Error('Read failed'); + }); } diff --git a/src/main/resources/templates/reservation-mine.html b/src/main/resources/templates/reservation-mine.html index 37703211b..6341abd67 100644 --- a/src/main/resources/templates/reservation-mine.html +++ b/src/main/resources/templates/reservation-mine.html @@ -53,6 +53,9 @@

내 예약

날짜 시간 상태 + 대기 취소 + paymentKey + 결제금액 diff --git a/src/test/java/roomescape/fixture/PaymentFixture.java b/src/test/java/roomescape/fixture/PaymentFixture.java index 216ef4dde..1d8505750 100644 --- a/src/test/java/roomescape/fixture/PaymentFixture.java +++ b/src/test/java/roomescape/fixture/PaymentFixture.java @@ -1,13 +1,13 @@ package roomescape.fixture; -import static roomescape.fixture.MemberFixture.DEFAULT_MEMBER; +import static roomescape.fixture.ReservationFixture.DEFAULT_RESERVATION; import roomescape.domain.payment.Payment; import roomescape.dto.PaymentApproveRequest; public class PaymentFixture { public static final Payment DEFAULT_PAYMENT_WITHOUT_ID = new Payment( - "orderId", "paymentKey", 1000, DEFAULT_MEMBER); + null, "orderId", "paymentKey", 1000, DEFAULT_RESERVATION); public static final PaymentApproveRequest DEFAULT_APPROVE_REQUEST = new PaymentApproveRequest( "paymentKey", "orderId", 1000 ); diff --git a/src/test/java/roomescape/repository/CollectionPaymentRepository.java b/src/test/java/roomescape/repository/CollectionPaymentRepository.java index d70d50aee..388d59678 100644 --- a/src/test/java/roomescape/repository/CollectionPaymentRepository.java +++ b/src/test/java/roomescape/repository/CollectionPaymentRepository.java @@ -2,7 +2,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; +import roomescape.domain.Reservation; import roomescape.domain.payment.Payment; public class CollectionPaymentRepository implements PaymentRepository { @@ -21,4 +23,31 @@ public Payment save(Payment payment) { payments.add(saved); return saved; } + + @Override + public List findAllByReservationIn(List reservations) { + return payments.stream() + .filter(payment -> reservations.contains(payment.getReservation())) + .toList(); + } + + @Override + public Optional findByOrderIdAndPaymentKey(String orderId, String paymentKey) { + return payments.stream() + .filter(payment -> payment.getOrderId().equals(orderId)) + .filter(payment -> payment.getPaymentKey().equals(paymentKey)) + .findFirst(); + } + + @Override + public Optional findByReservationId(Long reservationId) { + return payments.stream() + .filter(payment -> reservationId.equals(payment.getReservation().getId())) + .findFirst(); + } + + @Override + public void deleteByReservationId(Long reservationId) { + payments.removeIf(payment -> reservationId.equals(payment.getReservation().getId())); + } } diff --git a/src/test/java/roomescape/repository/DatabaseCleaner.java b/src/test/java/roomescape/repository/DatabaseCleaner.java index c8ca2f12c..10825f5e5 100644 --- a/src/test/java/roomescape/repository/DatabaseCleaner.java +++ b/src/test/java/roomescape/repository/DatabaseCleaner.java @@ -10,6 +10,8 @@ public class DatabaseCleaner { private JdbcTemplate jdbcTemplate; public void clean() { + jdbcTemplate.update("DELETE FROM payment"); + jdbcTemplate.update("ALTER TABLE payment ALTER COLUMN id RESTART WITH 1"); jdbcTemplate.update("DELETE FROM reservation_waiting"); jdbcTemplate.update("ALTER TABLE reservation_waiting ALTER COLUMN id RESTART WITH 1"); jdbcTemplate.update("DELETE FROM reservation"); diff --git a/src/test/java/roomescape/repository/JpaPaymentRepositoryTest.java b/src/test/java/roomescape/repository/JpaPaymentRepositoryTest.java index 2334dc72f..4df852805 100644 --- a/src/test/java/roomescape/repository/JpaPaymentRepositoryTest.java +++ b/src/test/java/roomescape/repository/JpaPaymentRepositoryTest.java @@ -1,23 +1,63 @@ package roomescape.repository; import static roomescape.fixture.PaymentFixture.DEFAULT_PAYMENT_WITHOUT_ID; +import static roomescape.fixture.ReservationFixture.DEFAULT_RESERVATION; +import static roomescape.fixture.ReservationTimeFixture.DEFAULT_TIME; +import static roomescape.fixture.ThemeFixture.DEFAULT_THEME; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; class JpaPaymentRepositoryTest extends DatabaseClearBeforeEachTest { @Autowired private PaymentRepository paymentRepository; + @Autowired + private ReservationRepository reservationRepository; + @Autowired + private ReservationTimeRepository reservationTimeRepository; + @Autowired + private ThemeRepository themeRepository; + + @Override + public void doAfterClear() { + reservationTimeRepository.save(DEFAULT_TIME); + themeRepository.save(DEFAULT_THEME); + } @Test @DisplayName("Payment를 잘 저장하는지 확인한다.") void save() { + reservationRepository.save(DEFAULT_RESERVATION); var saved = paymentRepository.save(DEFAULT_PAYMENT_WITHOUT_ID); Assertions.assertThat(saved.getId()) .isNotNull(); } + + @Test + @DisplayName("orderId와 paymentKey로 잘 조회하는지 확인") + void findByOrderIdAndPaymentKey() { + reservationRepository.save(DEFAULT_RESERVATION); + var saved = paymentRepository.save(DEFAULT_PAYMENT_WITHOUT_ID); + + Assertions.assertThat(paymentRepository.findByOrderIdAndPaymentKey( + DEFAULT_PAYMENT_WITHOUT_ID.getOrderId(), DEFAULT_PAYMENT_WITHOUT_ID.getPaymentKey() + )).contains(saved); + } + + @Test + @Transactional + @DisplayName("reservationId로 잘 삭제하는지 확인") + void deleteByReservationId() { + var reservation = reservationRepository.save(DEFAULT_RESERVATION); + paymentRepository.save(DEFAULT_PAYMENT_WITHOUT_ID); + + paymentRepository.deleteByReservationId(reservation.getId()); + Assertions.assertThat(paymentRepository.findByReservationId(reservation.getId())) + .isEmpty(); + } } diff --git a/src/test/java/roomescape/repository/JpaReservationTimeRepositoryTest.java b/src/test/java/roomescape/repository/JpaReservationTimeRepositoryTest.java index 2e62c3891..17b976027 100644 --- a/src/test/java/roomescape/repository/JpaReservationTimeRepositoryTest.java +++ b/src/test/java/roomescape/repository/JpaReservationTimeRepositoryTest.java @@ -48,7 +48,7 @@ void findAll() { } @Test - @DisplayName("ReservationTime 을 잘 지우하는지 확인한다.") + @DisplayName("ReservationTime 을 잘 지우는지 확인한다.") void delete() { List beforeSaveAndDelete = reservationTimeRepository.findAll(); reservationTimeRepository.save(DEFAULT_TIME); diff --git a/src/test/java/roomescape/service/PaymentServiceTest.java b/src/test/java/roomescape/service/PaymentServiceTest.java index 388e3d05f..df442e2d5 100644 --- a/src/test/java/roomescape/service/PaymentServiceTest.java +++ b/src/test/java/roomescape/service/PaymentServiceTest.java @@ -40,7 +40,7 @@ void approveFailWhenNotFoundMember() { @DisplayName("외부 결제 승인 API 호출이 실패할 경우 예외가 발생하는지 확인") void approveFailWhenPaymentApiFail() { PaymentClient paymentClient = Mockito.mock(PaymentClient.class); - Mockito.when(paymentClient.approve(DEFAULT_APPROVE_REQUEST, DEFAULT_MEMBER)) + Mockito.when(paymentClient.approve(DEFAULT_APPROVE_REQUEST)) .thenThrow(ApiCallException.class); PaymentRepository paymentRepository = new CollectionPaymentRepository(); diff --git a/src/test/java/roomescape/service/ReservationServiceTest.java b/src/test/java/roomescape/service/ReservationServiceTest.java index a60a5fe56..9a180fa9d 100644 --- a/src/test/java/roomescape/service/ReservationServiceTest.java +++ b/src/test/java/roomescape/service/ReservationServiceTest.java @@ -12,17 +12,21 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import roomescape.domain.Reservation; import roomescape.dto.ReservationRequest; import roomescape.dto.ReservationResponse; import roomescape.exception.ExceptionType; import roomescape.exception.RoomescapeException; +import roomescape.fixture.PaymentFixture; import roomescape.fixture.ReservationFixture; import roomescape.fixture.ReservationWaitingFixture; import roomescape.repository.CollectionMemberRepository; +import roomescape.repository.CollectionPaymentRepository; import roomescape.repository.CollectionReservationRepository; import roomescape.repository.CollectionReservationTimeRepository; import roomescape.repository.CollectionReservationWaitingRepository; import roomescape.repository.CollectionThemeRepository; +import roomescape.repository.PaymentRepository; import roomescape.repository.ReservationRepository; import roomescape.repository.ReservationWaitingRepository; import roomescape.service.finder.ReservationFinder; @@ -34,6 +38,7 @@ class ReservationServiceTest { private CollectionThemeRepository themeRepository; private CollectionMemberRepository memberRepository; private ReservationWaitingRepository waitingRepository; + private PaymentRepository paymentRepository; @BeforeEach void initService() { @@ -42,10 +47,11 @@ void initService() { memberRepository = new CollectionMemberRepository(); waitingRepository = new CollectionReservationWaitingRepository(); reservationRepository = new CollectionReservationRepository(); + paymentRepository = new CollectionPaymentRepository(); ReservationFinder reservationFinder = new ReservationFinder(reservationRepository, reservationTimeRepository, memberRepository, themeRepository); reservationService = new ReservationService(reservationRepository, waitingRepository, reservationFinder, - memberRepository); + memberRepository, paymentRepository); } @Test @@ -98,7 +104,7 @@ private void initServiceWithMember() { ReservationFinder reservationFinder = new ReservationFinder(reservationRepository, reservationTimeRepository, memberRepository, themeRepository); reservationService = new ReservationService(reservationRepository, waitingRepository, reservationFinder, - memberRepository); + memberRepository, paymentRepository); } @Test @@ -163,4 +169,20 @@ void changeReservationWhenDeletedReservationHasWaiting() { Assertions.assertThat(reservationMemberIsAdmin) .isTrue(); } + + @Test + @DisplayName("MemberId로 예약을 찾을 때 결제 정보도 잘 가져오는지 확인") + void findByMemberIdWithPayment() { + initServiceWithMember(); + reservationTimeRepository.save(DEFAULT_TIME); + themeRepository.save(DEFAULT_THEME); + + Reservation saved = reservationRepository.save(ReservationFixture.DEFAULT_RESERVATION); + paymentRepository.save(PaymentFixture.DEFAULT_PAYMENT_WITHOUT_ID); + + String savedPaymentKey = reservationService.findByMemberId(saved.getId()) + .get(0).paymentKey(); + Assertions.assertThat(savedPaymentKey) + .isEqualTo(PaymentFixture.DEFAULT_PAYMENT_WITHOUT_ID.getPaymentKey()); + } } diff --git a/src/test/java/roomescape/service/mapper/LoginMemberReservationResponseMapperTest.java b/src/test/java/roomescape/service/mapper/LoginMemberReservationResponseMapperTest.java index 7463a3a85..3df558c21 100644 --- a/src/test/java/roomescape/service/mapper/LoginMemberReservationResponseMapperTest.java +++ b/src/test/java/roomescape/service/mapper/LoginMemberReservationResponseMapperTest.java @@ -23,7 +23,7 @@ void toResponse() { DEFAULT_THEME.getName(), DEFAULT_RESERVATION.getDate(), DEFAULT_RESERVATION.getTime(), - "예약" + "예약", null, null )); } @@ -39,7 +39,7 @@ void from() { DEFAULT_THEME.getName(), DEFAULT_RESERVATION.getDate(), DEFAULT_RESERVATION.getTime(), - "1번째 예약 대기" + "1번째 예약 대기", null, null )); } }