From 91be72fb0326586ff1e6631c17d799d85d32295f Mon Sep 17 00:00:00 2001 From: Nguyen Thanh Duy <62139508+1119-DuyNguyen@users.noreply.github.com> Date: Fri, 19 Jul 2024 22:49:10 +0700 Subject: [PATCH] Mbc 41 task fix bug swagger and verify bill services by test cases (#24) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: bug login, swagger https * test: 50% bill + projection showtime * test: 100% bill service unit testing * refactor: add projection for rooms * fix: application not loaded integration testing * test: add test crash controller refactor: separate error info ( use for swagger later) * add chore file * test: add integration test for creating bill success * test: 100% coverage bills * hotfix: fix reference and error information of swagger * hotfix: fix security tickets --------- Co-authored-by: Tiền Minh Vy --- document/bill-100-coverage.png | Bin 0 -> 43147 bytes pom.xml | 92 +- .../MovieBookingApplication.java | 4 +- .../movie_booking/api/CrashController.java | 29 + .../movie_booking/api/RootController.java | 16 +- .../movie_booking/api/bill/Bill.java | 16 +- .../api/bill/BillController.java | 25 +- .../api/bill/BillModelAssembler.java | 21 +- .../api/bill/BillRepository.java | 5 +- .../api/bill/{dto => }/BillRequestDTO.java | 3 +- .../movie_booking/api/bill/BillService.java | 21 +- .../movie_booking/api/movie/ImageService.java | 31 + .../movie/elastic/MovieElasticController.java | 7 +- .../movie/elastic/MovieElasticService.java | 35 +- .../api/movie/elastic/MovieSearch.java | 1 + .../elastic/MovieSearchModelAssembler.java | 4 +- .../movie/elastic/MovieSearchRepository.java | 37 +- .../movie_booking/api/role/Role.java | 2 +- .../movie_booking/api/room/Room.java | 1 - .../api/room/RoomInfoProjection.java | 20 + .../api/room/RoomRepository.java | 2 +- .../movie_booking/api/seat/Seat.java | 9 - .../movie_booking/api/showtime/Showtime.java | 5 +- .../api/showtime/ShowtimeProjection.java | 23 + .../api/showtime/ShowtimeRepository.java | 10 +- .../movie_booking/api/ticket/Ticket.java | 17 +- .../api/ticket/TicketRepository.java | 5 +- .../api/user/UserAuthUtilService.java | 43 + .../movie_booking/system/CrashController.java | 24 - .../system/config/SecurityConfig.java | 3 +- .../config/api/ApiRestConfiguration.java | 10 +- .../config/api/ProjectionConfiguration.java | 15 + .../config/api/SwaggerConfiguration.java | 73 +- .../system/config/auth/AuthServiceUtils.java | 16 + .../auth/AuthorizationServerConfig.java | 2 +- .../system/exception/ErrorInfo.java | 29 + .../exception/GlobalExceptionHandler.java | 70 +- src/main/resources/application.yaml | 12 +- .../MovieBookingApplicationIT.java | 19 + .../MovieBookingApplicationTests.java | 6 - .../movie_booking/api/CrashControllerIT.java | 32 + .../movie_booking/api/RootControllerIT.java | 46 + .../movie_booking/api/bill/BillIT.java | 152 ++ .../api/bill/BillModelAssemblerTest.java | 118 ++ .../api/bill/BillServiceTest.java | 305 +++- .../movie_booking/api/bill/BillTest.java | 152 ++ .../api/bill/DeleteBillTaskTest.java | 63 + .../api/showtime/ShowtimeIT.java | 169 ++ .../intergration/RestdocsExampleIT.java | 41 - .../model/BaseIntegrationTest.java | 29 +- .../system/container/BaseTestContainer.java | 42 +- src/test/resources/application.yaml | 4 +- src/test/resources/bill.sql | 18 + src/test/resources/db/data.sql | 1540 +++++++++++++++++ src/test/resources/db/schema.sql | 649 +++++++ src/test/resources/showtime.sql | 26 + 56 files changed, 3742 insertions(+), 407 deletions(-) create mode 100644 document/bill-100-coverage.png create mode 100644 src/main/java/com/tech_symfony/movie_booking/api/CrashController.java rename src/main/java/com/tech_symfony/movie_booking/api/bill/{dto => }/BillRequestDTO.java (92%) create mode 100644 src/main/java/com/tech_symfony/movie_booking/api/movie/ImageService.java create mode 100644 src/main/java/com/tech_symfony/movie_booking/api/room/RoomInfoProjection.java create mode 100644 src/main/java/com/tech_symfony/movie_booking/api/showtime/ShowtimeProjection.java create mode 100644 src/main/java/com/tech_symfony/movie_booking/api/user/UserAuthUtilService.java delete mode 100644 src/main/java/com/tech_symfony/movie_booking/system/CrashController.java create mode 100644 src/main/java/com/tech_symfony/movie_booking/system/config/api/ProjectionConfiguration.java create mode 100644 src/main/java/com/tech_symfony/movie_booking/system/config/auth/AuthServiceUtils.java create mode 100644 src/main/java/com/tech_symfony/movie_booking/system/exception/ErrorInfo.java create mode 100644 src/test/java/com/tech_symfony/movie_booking/MovieBookingApplicationIT.java create mode 100644 src/test/java/com/tech_symfony/movie_booking/api/CrashControllerIT.java create mode 100644 src/test/java/com/tech_symfony/movie_booking/api/RootControllerIT.java create mode 100644 src/test/java/com/tech_symfony/movie_booking/api/bill/BillIT.java create mode 100644 src/test/java/com/tech_symfony/movie_booking/api/bill/BillModelAssemblerTest.java create mode 100644 src/test/java/com/tech_symfony/movie_booking/api/bill/BillTest.java create mode 100644 src/test/java/com/tech_symfony/movie_booking/api/bill/DeleteBillTaskTest.java create mode 100644 src/test/java/com/tech_symfony/movie_booking/api/showtime/ShowtimeIT.java delete mode 100644 src/test/java/com/tech_symfony/movie_booking/intergration/RestdocsExampleIT.java create mode 100644 src/test/resources/bill.sql create mode 100644 src/test/resources/db/data.sql create mode 100644 src/test/resources/db/schema.sql create mode 100644 src/test/resources/showtime.sql diff --git a/document/bill-100-coverage.png b/document/bill-100-coverage.png new file mode 100644 index 0000000000000000000000000000000000000000..3ea4085ea2300b2e583c5a6f9a6e8f4c6e3d4897 GIT binary patch literal 43147 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¬|K3{o(CS)hY82qULN$I?$>>)TMHOL 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 t6DwLQEhxPv8qP8Cs91.19.8 test + + org.testcontainers + mysql + 1.19.8 + test + org.testcontainers @@ -298,41 +304,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - maven-resources-plugin @@ -353,24 +325,6 @@ - - - - - - - - - - - - - - - - - - org.apache.maven.plugins maven-compiler-plugin @@ -391,5 +345,39 @@ + + + unit-test + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/Test*.java + + + + + + + + integration-test + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*IT.java + + + + + + + diff --git a/src/main/java/com/tech_symfony/movie_booking/MovieBookingApplication.java b/src/main/java/com/tech_symfony/movie_booking/MovieBookingApplication.java index fd18d04..0a7c2bf 100644 --- a/src/main/java/com/tech_symfony/movie_booking/MovieBookingApplication.java +++ b/src/main/java/com/tech_symfony/movie_booking/MovieBookingApplication.java @@ -13,8 +13,8 @@ import java.util.UUID; @SpringBootApplication -@EnableJpaRepositories(includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = JpaRepository.class)) -@EnableElasticsearchRepositories(includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ElasticsearchRepository.class)) +//@EnableJpaRepositories(includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = JpaRepository.class)) +//@EnableElasticsearchRepositories(includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ElasticsearchRepository.class)) public class MovieBookingApplication { public static void main(String[] args) { diff --git a/src/main/java/com/tech_symfony/movie_booking/api/CrashController.java b/src/main/java/com/tech_symfony/movie_booking/api/CrashController.java new file mode 100644 index 0000000..5efae2a --- /dev/null +++ b/src/main/java/com/tech_symfony/movie_booking/api/CrashController.java @@ -0,0 +1,29 @@ + +package com.tech_symfony.movie_booking.api; + +import com.tech_symfony.movie_booking.system.exception.ErrorInfo; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Controller used to showcase what happens when an exception is thrown + */ +@RestController +class CrashController { + + @Operation( + summary = "Kiểm thử khả năng xử lý lỗi của ứng dụng" + ) + + @GetMapping("/oups") + public ResponseEntity triggerException() { + throw new RuntimeException( + "Expected: controller used to showcase what " + "happens when an exception is thrown"); + } + +} diff --git a/src/main/java/com/tech_symfony/movie_booking/api/RootController.java b/src/main/java/com/tech_symfony/movie_booking/api/RootController.java index ecf86cf..e121705 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/RootController.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/RootController.java @@ -1,31 +1,25 @@ package com.tech_symfony.movie_booking.api; -import com.tech_symfony.movie_booking.api.bill.Bill; import com.tech_symfony.movie_booking.api.cinema.Cinema; import com.tech_symfony.movie_booking.api.movie.Movie; -import com.tech_symfony.movie_booking.api.movie.MovieRepository; import com.tech_symfony.movie_booking.api.showtime.Showtime; +import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks; -import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; -import org.springframework.hateoas.LinkRelation; import org.springframework.hateoas.RepresentationModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.ArrayList; - -import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; -import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; - @RestController @RequiredArgsConstructor public class RootController { private final RepositoryEntityLinks entityLinks; + @Operation( + summary = "Đường dẫn gốc", + description = "Từ đường dẫn này sẽ đi đến những đường dẫn khác dựa theo hành vi người dùng" + ) @GetMapping RepresentationModel index() { diff --git a/src/main/java/com/tech_symfony/movie_booking/api/bill/Bill.java b/src/main/java/com/tech_symfony/movie_booking/api/bill/Bill.java index 4989379..98257b4 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/bill/Bill.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/bill/Bill.java @@ -1,5 +1,6 @@ package com.tech_symfony.movie_booking.api.bill; +import com.fasterxml.jackson.annotation.JsonProperty; import com.tech_symfony.movie_booking.api.ticket.Ticket; import com.tech_symfony.movie_booking.api.user.User; import com.tech_symfony.movie_booking.model.BaseUUIDEntity; @@ -38,7 +39,6 @@ public class Bill extends BaseUUIDEntity { private Double total; @Column(name = "transaction_id") -// @NotEmpty(message = "Transaction id must not be empty") private String transactionId; @Column(name = "cancel_reason") @@ -46,26 +46,27 @@ public class Bill extends BaseUUIDEntity { private String cancelReason = ""; @Column(name = "cancel_date") + @Temporal(TemporalType.TIMESTAMP) private LocalDateTime cancelDate; - + //WARN: a collection doesn't contain tickets so avoiding N+1 problem @OneToMany( - mappedBy = "bill", - fetch = FetchType.LAZY, - cascade = CascadeType.ALL + cascade = CascadeType.PERSIST ) + @JoinColumn(name = "bill_id") private Set tickets; @ManyToOne @JoinColumn( name = "user_id", - nullable = false + nullable = false, + updatable = false ) @NotNull(message = "User must not be null") private User user; - @NotNull(message = "Gender must not be null") + @NotNull(message = "Status must not be null") @Enumerated(EnumType.ORDINAL) private BillStatus status = BillStatus.IN_PROGRESS; @@ -74,7 +75,6 @@ public void addTicket( ) { if (this.tickets == null) this.tickets = new HashSet<>(); this.tickets.add(ticket); - ticket.setBill(this); } @Transient diff --git a/src/main/java/com/tech_symfony/movie_booking/api/bill/BillController.java b/src/main/java/com/tech_symfony/movie_booking/api/bill/BillController.java index 9e52ead..c52117f 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/bill/BillController.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/bill/BillController.java @@ -1,8 +1,9 @@ package com.tech_symfony.movie_booking.api.bill; -import com.tech_symfony.movie_booking.api.bill.dto.BillRequestDTO; +import com.tech_symfony.movie_booking.api.user.UserAuthUtilService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.rest.webmvc.RepositoryRestController; @@ -11,21 +12,22 @@ import org.springframework.hateoas.Link; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.web.bind.annotation.*; -import java.security.Principal; import java.util.UUID; + @RequiredArgsConstructor @RepositoryRestController @ResponseBody @SecurityRequirement(name = "security_auth") +@Tag(name = "bill-entity-controller") public class BillController { private final BillService billService; private final RepositoryEntityLinks entityLinks; private final BillModelAssembler billModelAssembler; + private final UserAuthUtilService userAuthUtilService; @Operation( summary = "Thêm hóa đơn kèm vé trước khi thanh toán", @@ -35,11 +37,11 @@ public class BillController { ) @PostMapping(value = "/bills") @ResponseStatus(HttpStatus.CREATED) - public ResponseEntity> create( - @Valid @RequestBody BillRequestDTO dataRaw, - Principal principal + public ResponseEntity> create( + @Valid @RequestBody BillRequestDTO dataRaw ) { - Bill newBill = billService.create(dataRaw, principal.getName()); + + Bill newBill = billService.create(dataRaw, userAuthUtilService.getCurrentUsername()); Link link = entityLinks.linkToItemResource(Bill.class, newBill.getId()); return ResponseEntity .created(link.toUri()) @@ -54,22 +56,21 @@ public ResponseEntity> create( "sẽ được nêu rõ. " ) @PutMapping(value = "/bills/{id}/payment") - public EntityModel pay( + public EntityModel pay( @PathVariable UUID id ) { - return billModelAssembler.toModel(billService.pay(id)); + return billModelAssembler.toModel(billService.pay(id, userAuthUtilService.getCurrentUsername())); } @Operation( summary = "Cập nhập thanh toán", - description = "Api được gọi khi khách hàng vào rạp, nhân viên sẽ quét QR và cập nhập trạng thái đơn hàng đã sử dụng thành công" + description = "Api được gọi khi khách hàng vào rạp, nhân viên sẽ quét và cập nhập trạng thái vé đã sử dụng thành công" ) @PutMapping(value = "/bills/{id}") - public EntityModel updateStatus( + public EntityModel updateStatus( @PathVariable UUID id ) { - // TODO: chưa ràng buộc thời hạn nếu như vé bị outdated return billModelAssembler.toModel(billService.updateStatus(id)); } diff --git a/src/main/java/com/tech_symfony/movie_booking/api/bill/BillModelAssembler.java b/src/main/java/com/tech_symfony/movie_booking/api/bill/BillModelAssembler.java index 30315cc..f3f59d0 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/bill/BillModelAssembler.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/bill/BillModelAssembler.java @@ -1,44 +1,43 @@ package com.tech_symfony.movie_booking.api.bill; -import jakarta.servlet.ServletException; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.data.projection.ProjectionFactory; +import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; import org.springframework.hateoas.server.RepresentationModelAssembler; import org.springframework.stereotype.Component; -import java.io.IOException; - import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; @Component -@AllArgsConstructor -class BillModelAssembler implements RepresentationModelAssembler> { +@RequiredArgsConstructor +class BillModelAssembler implements RepresentationModelAssembler> { private final RepositoryEntityLinks entityLinks; + private final ProjectionFactory projectionFactory; @Override - public EntityModel toModel(Bill bill) { + public EntityModel toModel(Bill bill) { Link linkNewResource = entityLinks.linkToItemResource(Bill.class, bill.getId()); Link linkCollectionResource = entityLinks.linkToCollectionResource(Bill.class); // Unconditional links to single-item resource and aggregate root + BillInfoProjection projection = projectionFactory.createProjection(BillInfoProjection.class, bill); - EntityModel billModel = EntityModel.of( - bill, + EntityModel billModel = EntityModel.of( + projection, linkNewResource, linkCollectionResource); // Conditional links based on state of the order if (bill.getStatus() == BillStatus.IN_PROGRESS) { -// orderModel.add(linkTo(methodOn(BillController.class).cancel(bill.getId())).withRel("cancel")); billModel.add(linkTo(methodOn(BillController.class).pay(bill.getId())).withRel("pay")); } - return billModel; } diff --git a/src/main/java/com/tech_symfony/movie_booking/api/bill/BillRepository.java b/src/main/java/com/tech_symfony/movie_booking/api/bill/BillRepository.java index 3ed39fa..eab0740 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/bill/BillRepository.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/bill/BillRepository.java @@ -4,15 +4,18 @@ import com.tech_symfony.movie_booking.model.BaseAuthenticatedRepository; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import org.springframework.data.rest.core.annotation.RestResource; +import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PreAuthorize; import java.util.Optional; import java.util.UUID; @RepositoryRestResource(excerptProjection = BillInfoProjection.class) -@PreAuthorize("hasAuthority( 'READ_BILL')") public interface BillRepository extends BaseAuthenticatedRepository { + @Override + @PostAuthorize("@userAuthUtilService.isOwner(returnObject.get().getUser().getId()) or hasAuthority('READ_BILL')") + Optional findById(UUID uuid); @Override @RestResource(exported = false) diff --git a/src/main/java/com/tech_symfony/movie_booking/api/bill/dto/BillRequestDTO.java b/src/main/java/com/tech_symfony/movie_booking/api/bill/BillRequestDTO.java similarity index 92% rename from src/main/java/com/tech_symfony/movie_booking/api/bill/dto/BillRequestDTO.java rename to src/main/java/com/tech_symfony/movie_booking/api/bill/BillRequestDTO.java index 6fbf433..4ceac95 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/bill/dto/BillRequestDTO.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/bill/BillRequestDTO.java @@ -1,4 +1,4 @@ -package com.tech_symfony.movie_booking.api.bill.dto; +package com.tech_symfony.movie_booking.api.bill; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotEmpty; @@ -18,7 +18,6 @@ public class BillRequestDTO { // private int changedPoint; @NotNull(message = "Showtime id MUST not be null") - @Min(1) private int showtimeId; @NotEmpty(message = "seat MUST not be empty") diff --git a/src/main/java/com/tech_symfony/movie_booking/api/bill/BillService.java b/src/main/java/com/tech_symfony/movie_booking/api/bill/BillService.java index c6fa879..0203133 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/bill/BillService.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/bill/BillService.java @@ -1,6 +1,5 @@ package com.tech_symfony.movie_booking.api.bill; -import com.tech_symfony.movie_booking.api.bill.dto.BillRequestDTO; import com.tech_symfony.movie_booking.api.bill.vnpay.VnpayService; import com.tech_symfony.movie_booking.api.seat.Seat; import com.tech_symfony.movie_booking.api.seat.SeatRepository; @@ -29,7 +28,7 @@ public interface BillService { Bill create(BillRequestDTO dataRaw, String username); - Bill pay(UUID billID); + Bill pay(UUID billID, String username); Bill updateStatus(UUID billId); @@ -56,13 +55,13 @@ class DefaultBillService implements BillService { private final VnpayService vnpayService; - private void checkSeat(Showtime showtime, List seatList) { + private Boolean checkSeat(Showtime showtime, List seatList) { Set t = ticketRepository.findByShowtimeAndSeatIn(showtime, seatList); if (t.size() > 0) { throw new ForbidenMethodControllerException("Seat already reserved"); } - + return true; } private Double getPriceOfSeat(Seat seat) { @@ -101,18 +100,17 @@ public Bill create(BillRequestDTO billRequestDTO, String username) { ; Timer timer = new Timer(); -// bill.setId(UUID.fromString("e772c5cc-982d-45a2-a3d8-0e0a7e735fdf")); -// return bill; + Bill savedBill = billRepository.save(bill); savedBill.setVnpayUrl(vnpayService.doPost(savedBill)); timer.schedule(new DeleteBillTask(billRepository, savedBill.getId()), payExpiration); return savedBill; } + @Override - public Bill pay(UUID billID) { - Authentication auth = SecurityContextHolder.getContext().getAuthentication(); - User user = userRepository.findByEmail(auth.getName()) + public Bill pay(UUID billID, String username) { + User user = userRepository.findByEmail(username) .orElseThrow(() -> new UsernameNotFoundException("Conflict")); Bill bill = billRepository.findByIdAndUser(billID, user) .orElseThrow(() -> new ResourceNotFoundException("Bill is not exits")); @@ -126,11 +124,14 @@ public Bill pay(UUID billID) { } - @PreAuthorize("hasAuthority( 'SAVE_BILL')") + @PreAuthorize("hasAuthority('SAVE_BILL')") public Bill updateStatus(UUID billId) { Bill bill = billRepository.findById(billId) .orElseThrow(() -> new ResourceNotFoundException("Bill not found")); + if (bill.getStatus() != BillStatus.HOLDING) { + throw new ForbidenMethodControllerException("Bill is not holding"); + } bill.setStatus(BillStatus.COMPLETED); return billRepository.save(bill); } diff --git a/src/main/java/com/tech_symfony/movie_booking/api/movie/ImageService.java b/src/main/java/com/tech_symfony/movie_booking/api/movie/ImageService.java new file mode 100644 index 0000000..4aef86d --- /dev/null +++ b/src/main/java/com/tech_symfony/movie_booking/api/movie/ImageService.java @@ -0,0 +1,31 @@ +package com.tech_symfony.movie_booking.api.movie; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +public interface ImageService { +} + +class TelegramService implements ImageService { + + @Value("${TELEGRAM_TOKEN}") + private String botToken; +} +// +//@RequiredArgsConstructor +//@Service +//class DiscordService implements ImageService { +// +// @Value("${discord.token}") +// private String botToken; +// +// @Value("${discord.poster_channel}") +// private String posterChannel; +// +// @Value("${discord.image_channel}") +// private String imageChannel; +// +// @Value("${discord.avatar_channel}") +// private String avatarChannel; +//} diff --git a/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieElasticController.java b/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieElasticController.java index cb8c2bf..60c6f26 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieElasticController.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieElasticController.java @@ -8,15 +8,15 @@ import org.springframework.data.rest.webmvc.RepositoryRestController; import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; import java.util.Optional; @RequiredArgsConstructor @RepositoryRestController @ResponseBody +@Controller public class MovieElasticController { private final MovieElasticService movieElasticService; @@ -28,6 +28,7 @@ public class MovieElasticController { ) @GetMapping("/movies/search") @ResponseBody + @ExceptionHandler(Exception.class) public CollectionModel> search(Pageable pageable, @RequestParam Optional name) { Page movies = movieElasticService.search(PageRequest.of( diff --git a/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieElasticService.java b/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieElasticService.java index 9b3ba75..e49d375 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieElasticService.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieElasticService.java @@ -3,6 +3,10 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.core.SearchHitSupport; +import org.springframework.data.elasticsearch.core.SearchPage; +import org.springframework.data.elasticsearch.core.query.*; import org.springframework.stereotype.Service; import java.util.Optional; @@ -16,10 +20,37 @@ public interface MovieElasticService { @RequiredArgsConstructor class DefaultMovieElasticService implements MovieElasticService { - private final MovieSearchRepository movieSearchRepository; +// private final MovieSearchRepository movieSearchRepository; + + private final ElasticsearchOperations operations; @Override public Page search(Pageable pageable, Optional name) { - return movieSearchRepository.findByName(name.orElse(""), pageable); + Query query = new StringQuery("{" + + " \"bool\": {" + + " \"should\": [" + + " {" + + " \"match\": {" + + " \"name\": {" + + " \"query\": \""+ name.orElse("") +"\"," + + " \"fuzziness\": \"AUTO\"" + + " }" + + " }" + + " }, " + + " {" + + " \"wildcard\": {" + + " \"name\": {" + + " \"value\": \""+ name.orElse("") + "?*\"" + + " }" + + " }" + + " }" + + " ]" + + " }" + + "}", pageable); + SearchPage movies = SearchHitSupport.searchPageFor( + operations.search(query, MovieSearch.class), + pageable + ); + return (Page) SearchHitSupport.unwrapSearchHits(movies); } } diff --git a/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieSearch.java b/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieSearch.java index 0896c18..3aeddc5 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieSearch.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieSearch.java @@ -1,6 +1,7 @@ package com.tech_symfony.movie_booking.api.movie.elastic; import com.tech_symfony.movie_booking.api.movie.Movie; +import jakarta.persistence.Id; import org.springframework.data.elasticsearch.annotations.Document; @Document(indexName = "movies") diff --git a/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieSearchModelAssembler.java b/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieSearchModelAssembler.java index cfc1f2b..b511cad 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieSearchModelAssembler.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieSearchModelAssembler.java @@ -16,8 +16,8 @@ public class MovieSearchModelAssembler implements RepresentationModelAssembler toModel(MovieSearch movieSearch) { - Link linkResource = entityLinks.linkToItemResource(Movie.class, movieSearch.getId()); - Link linkCollectionResource = entityLinks.linkToCollectionResource(Movie.class); + Link linkResource = entityLinks.linkToItemResource(MovieSearch.class, movieSearch.getId()); + Link linkCollectionResource = entityLinks.linkToCollectionResource(MovieSearch.class); return EntityModel.of( movieSearch, diff --git a/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieSearchRepository.java b/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieSearchRepository.java index e2b03b3..ea60396 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieSearchRepository.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/movie/elastic/MovieSearchRepository.java @@ -1,20 +1,17 @@ -package com.tech_symfony.movie_booking.api.movie.elastic; - -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.elasticsearch.annotations.Query; -import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; - -public interface MovieSearchRepository extends ElasticsearchRepository { - @Query(""" - { - "match": { - "name": { - "query": "?0", - "fuzziness" : "AUTO" - } - } - } - """) - Page findByName(String name, Pageable pageable); -} +//package com.tech_symfony.movie_booking.api.movie.elastic; +// +//import org.springframework.data.domain.Page; +//import org.springframework.data.domain.Pageable; +//import org.springframework.data.elasticsearch.annotations.Query; +//import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +// +//public interface MovieSearchRepository extends ElasticsearchRepository { +// @Query(""" +// { +// "wildcard": { +// "name": "?0*" +// } +// } +// """) +// Page findByName(String name, Pageable pageable); +//} diff --git a/src/main/java/com/tech_symfony/movie_booking/api/role/Role.java b/src/main/java/com/tech_symfony/movie_booking/api/role/Role.java index 3f32e2f..b59033c 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/role/Role.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/role/Role.java @@ -30,7 +30,7 @@ public class Role extends NamedEntity { // @RestResource(exported = false) // private Set users; - @ManyToMany(fetch = FetchType.LAZY) + @ManyToMany(fetch = FetchType.EAGER) @JoinTable( name = "role_permission", diff --git a/src/main/java/com/tech_symfony/movie_booking/api/room/Room.java b/src/main/java/com/tech_symfony/movie_booking/api/room/Room.java index 1c00089..69cfc4a 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/room/Room.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/room/Room.java @@ -35,7 +35,6 @@ public class Room extends NamedEntity { fetch = FetchType.LAZY, cascade = CascadeType.ALL ) - @JsonManagedReference private Set showtimes; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) diff --git a/src/main/java/com/tech_symfony/movie_booking/api/room/RoomInfoProjection.java b/src/main/java/com/tech_symfony/movie_booking/api/room/RoomInfoProjection.java new file mode 100644 index 0000000..d0740c8 --- /dev/null +++ b/src/main/java/com/tech_symfony/movie_booking/api/room/RoomInfoProjection.java @@ -0,0 +1,20 @@ +package com.tech_symfony.movie_booking.api.room; + +import com.tech_symfony.movie_booking.api.user.User; +import com.tech_symfony.movie_booking.api.user.attribute.Gender; +import com.tech_symfony.movie_booking.model.NamedProjection; +import org.springframework.data.rest.core.config.Projection; + +import java.sql.Date; +import java.time.LocalDateTime; + +@Projection(name = "roomProjection", types = {Room.class}) +interface RoomInfoProjection extends NamedProjection { + + + Integer getTotalSeats(); + + Integer getAvailableSeats(); + + +} diff --git a/src/main/java/com/tech_symfony/movie_booking/api/room/RoomRepository.java b/src/main/java/com/tech_symfony/movie_booking/api/room/RoomRepository.java index da48aeb..b83d461 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/room/RoomRepository.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/room/RoomRepository.java @@ -4,7 +4,7 @@ import org.springframework.data.rest.core.annotation.RepositoryRestResource; import org.springframework.security.access.prepost.PreAuthorize; -@RepositoryRestResource +@RepositoryRestResource(excerptProjection = RoomInfoProjection.class) public interface RoomRepository extends BaseAuthenticatedRepository { @PreAuthorize("hasAuthority( 'SAVE_ROOM')") diff --git a/src/main/java/com/tech_symfony/movie_booking/api/seat/Seat.java b/src/main/java/com/tech_symfony/movie_booking/api/seat/Seat.java index 772faba..035353a 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/seat/Seat.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/seat/Seat.java @@ -50,15 +50,6 @@ public class Seat extends BaseEntity { private SeatType type; - @OneToMany( - mappedBy = "seat", - fetch = FetchType.LAZY, - cascade = CascadeType.PERSIST - ) - @JsonBackReference - - private Set tickets; - @Column(name = "row_name") @NotBlank(message = "seat room row must not be blank") diff --git a/src/main/java/com/tech_symfony/movie_booking/api/showtime/Showtime.java b/src/main/java/com/tech_symfony/movie_booking/api/showtime/Showtime.java index 01bff88..db8c8b6 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/showtime/Showtime.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/showtime/Showtime.java @@ -1,6 +1,5 @@ package com.tech_symfony.movie_booking.api.showtime; -import com.fasterxml.jackson.annotation.JsonBackReference; import com.tech_symfony.movie_booking.api.movie.Movie; import com.tech_symfony.movie_booking.api.room.Room; import com.tech_symfony.movie_booking.model.BaseEntity; @@ -46,18 +45,16 @@ public class Showtime extends BaseEntity { nullable = false ) @RestResource(exported = false) - @JsonBackReference private Movie movie; @NotEmpty(message = "showtime id must not be empty") - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn( name = "room_id", nullable = false ) @RestResource(exported = false) - @JsonBackReference private Room room; } diff --git a/src/main/java/com/tech_symfony/movie_booking/api/showtime/ShowtimeProjection.java b/src/main/java/com/tech_symfony/movie_booking/api/showtime/ShowtimeProjection.java new file mode 100644 index 0000000..912c170 --- /dev/null +++ b/src/main/java/com/tech_symfony/movie_booking/api/showtime/ShowtimeProjection.java @@ -0,0 +1,23 @@ +package com.tech_symfony.movie_booking.api.showtime; + +import com.tech_symfony.movie_booking.api.room.Room; +import com.tech_symfony.movie_booking.model.NamedProjection; + +import java.sql.Date; +import java.sql.Time; + +public interface ShowtimeProjection { + + Date getStartDate(); + + Time getStartTime(); + + Integer getRunningTime(); + + Boolean getStatus(); + + NamedProjection getMovie(); + + NamedProjection getRoom(); + +} diff --git a/src/main/java/com/tech_symfony/movie_booking/api/showtime/ShowtimeRepository.java b/src/main/java/com/tech_symfony/movie_booking/api/showtime/ShowtimeRepository.java index 8f50d3d..92d833f 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/showtime/ShowtimeRepository.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/showtime/ShowtimeRepository.java @@ -1,18 +1,10 @@ package com.tech_symfony.movie_booking.api.showtime; -import com.tech_symfony.movie_booking.api.cinema.Cinema; import com.tech_symfony.movie_booking.model.BaseAuthenticatedRepository; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; import org.springframework.security.access.prepost.PreAuthorize; -import java.util.Date; -import java.util.List; -import java.util.Optional; - -@RepositoryRestResource +@RepositoryRestResource(excerptProjection = ShowtimeProjection.class) public interface ShowtimeRepository extends BaseAuthenticatedRepository { @PreAuthorize("hasAuthority( 'SAVE_SHOWTIME')") diff --git a/src/main/java/com/tech_symfony/movie_booking/api/ticket/Ticket.java b/src/main/java/com/tech_symfony/movie_booking/api/ticket/Ticket.java index 5c865c4..a8243bb 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/ticket/Ticket.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/ticket/Ticket.java @@ -15,6 +15,8 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import lombok.*; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; import java.util.List; @@ -29,32 +31,21 @@ @AllArgsConstructor public class Ticket extends BaseUUIDEntity { - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) - @JoinColumn( - name = "bill_id", - nullable = false - ) - @NotNull(message = "Bill MUST not be null") - @JsonBackReference - private Bill bill; - - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) + @ManyToOne @JoinColumn( name = "showtime_id", nullable = false ) @NotNull(message = "Showtime MUST not be null") - @JsonManagedReference private Showtime showtime; - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) + @ManyToOne @JoinColumn( name = "seat_id", nullable = false ) @NotNull(message = "seat MUST not be null") - @JsonManagedReference private Seat seat; diff --git a/src/main/java/com/tech_symfony/movie_booking/api/ticket/TicketRepository.java b/src/main/java/com/tech_symfony/movie_booking/api/ticket/TicketRepository.java index 4139ca3..b06563a 100644 --- a/src/main/java/com/tech_symfony/movie_booking/api/ticket/TicketRepository.java +++ b/src/main/java/com/tech_symfony/movie_booking/api/ticket/TicketRepository.java @@ -3,14 +3,17 @@ import com.tech_symfony.movie_booking.api.seat.Seat; import com.tech_symfony.movie_booking.api.showtime.Showtime; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; +import org.springframework.data.rest.core.annotation.RestResource; import java.util.List; import java.util.Set; import java.util.UUID; +@RepositoryRestResource public interface TicketRepository extends JpaRepository { - + @RestResource(exported = false) Set findByShowtimeAndSeatIn( Showtime showtime, List seatList diff --git a/src/main/java/com/tech_symfony/movie_booking/api/user/UserAuthUtilService.java b/src/main/java/com/tech_symfony/movie_booking/api/user/UserAuthUtilService.java new file mode 100644 index 0000000..dc3e5b4 --- /dev/null +++ b/src/main/java/com/tech_symfony/movie_booking/api/user/UserAuthUtilService.java @@ -0,0 +1,43 @@ +package com.tech_symfony.movie_booking.api.user; + +import com.tech_symfony.movie_booking.api.bill.Bill; +import lombok.RequiredArgsConstructor; +import lombok.extern.java.Log; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import java.util.Optional; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +@Slf4j +public class UserAuthUtilService { + private final UserRepository userRepository; + + public User loadCurrentUser() { + User user = userRepository.findByEmail(getCurrentUsername()) + .orElseThrow(() -> new UsernameNotFoundException("Conflict")); + log.info("User: " + user.getId() + " with email " + user.getEmail() + " is authenticated"); + return user; + + } + + public String getCurrentUsername() { + return SecurityContextHolder.getContext().getAuthentication().getName(); + } + + // public boolean isOwnerBill(Optional billOptional) { +// if (billOptional.isEmpty()) { +// return false; +// } +// return getCurrentUser().getId().equals(billOptional.get().getUser().getId()); +// } + public boolean isOwner(UUID idObject) { + User user = loadCurrentUser(); + return user.getId().equals(idObject); + } +} diff --git a/src/main/java/com/tech_symfony/movie_booking/system/CrashController.java b/src/main/java/com/tech_symfony/movie_booking/system/CrashController.java deleted file mode 100644 index 246045c..0000000 --- a/src/main/java/com/tech_symfony/movie_booking/system/CrashController.java +++ /dev/null @@ -1,24 +0,0 @@ - -package com.tech_symfony.movie_booking.system; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; - -/** - * Controller used to showcase what happens when an exception is thrown - * - * @author Michael Isvy - *

- * Also see how a view that resolves to "error" has been added - * ("error.html"). - */ -@Controller -class CrashController { - - @GetMapping("/oups") - public String triggerException() { - throw new RuntimeException( - "Expected: controller used to showcase what " + "happens when an exception is thrown"); - } - -} diff --git a/src/main/java/com/tech_symfony/movie_booking/system/config/SecurityConfig.java b/src/main/java/com/tech_symfony/movie_booking/system/config/SecurityConfig.java index f40f4d1..6aea724 100644 --- a/src/main/java/com/tech_symfony/movie_booking/system/config/SecurityConfig.java +++ b/src/main/java/com/tech_symfony/movie_booking/system/config/SecurityConfig.java @@ -72,7 +72,7 @@ public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) // Authorize requests .authorizeHttpRequests((authorize) -> authorize //Getting public api - .requestMatchers(HttpMethod.GET, "movies/**", "cinemas", "seats", "seat-types").permitAll() + .requestMatchers(HttpMethod.GET, "movies/**", "cinemas", "seats", "showtimes", "rooms", "seat-types").permitAll() // .requestMatchers("/", "auth/**").permitAll() @@ -80,6 +80,7 @@ public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) .requestMatchers("/users").hasAuthority("READ_USER") //swagger docs .requestMatchers("/swagger-ui/**", "/v3/**", "/swagger-ui.html", "/openapi-3.0.yml").permitAll() + .requestMatchers("/tickets").denyAll() .anyRequest() .authenticated() diff --git a/src/main/java/com/tech_symfony/movie_booking/system/config/api/ApiRestConfiguration.java b/src/main/java/com/tech_symfony/movie_booking/system/config/api/ApiRestConfiguration.java index 1354024..3f6dbdd 100644 --- a/src/main/java/com/tech_symfony/movie_booking/system/config/api/ApiRestConfiguration.java +++ b/src/main/java/com/tech_symfony/movie_booking/system/config/api/ApiRestConfiguration.java @@ -1,11 +1,13 @@ package com.tech_symfony.movie_booking.system.config.api; import com.tech_symfony.movie_booking.api.bill.Bill; +import com.tech_symfony.movie_booking.api.ticket.Ticket; import org.springframework.context.annotation.Configuration; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.mapping.ExposureConfiguration; import org.springframework.data.rest.core.mapping.RepositoryDetectionStrategy; import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer; +import org.springframework.http.HttpMethod; import org.springframework.web.servlet.config.annotation.CorsRegistry; @Configuration @@ -19,7 +21,13 @@ public void configureRepositoryRestConfiguration( // config.disableDefaultExposure(); config.setRepositoryDetectionStrategy(RepositoryDetectionStrategy.RepositoryDetectionStrategies.ANNOTATED); ExposureConfiguration exposureConfiguration = config.getExposureConfiguration(); - exposureConfiguration.forDomainType(Bill.class).disablePutForCreation(); + exposureConfiguration.forDomainType(Ticket.class) + .withItemExposure(((metadata, httpMethods) + -> httpMethods.disable(HttpMethod.GET, HttpMethod.PUT, HttpMethod.POST, HttpMethod.PATCH, HttpMethod.DELETE) + )) + .withCollectionExposure(((metadata, httpMethods) + -> httpMethods.disable(HttpMethod.PUT, HttpMethod.POST, HttpMethod.PATCH, HttpMethod.DELETE) + )); // config.setExposeRepositoryMethodsByDefault(false); // config.getMetadataConfiguration().setAlpsEnabled(false); } diff --git a/src/main/java/com/tech_symfony/movie_booking/system/config/api/ProjectionConfiguration.java b/src/main/java/com/tech_symfony/movie_booking/system/config/api/ProjectionConfiguration.java new file mode 100644 index 0000000..5be2d2c --- /dev/null +++ b/src/main/java/com/tech_symfony/movie_booking/system/config/api/ProjectionConfiguration.java @@ -0,0 +1,15 @@ +package com.tech_symfony.movie_booking.system.config.api; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.projection.SpelAwareProxyProjectionFactory; + +@Configuration +public class ProjectionConfiguration { + + @Bean + public SpelAwareProxyProjectionFactory projectionFactory() { + return new SpelAwareProxyProjectionFactory(); + } + +} diff --git a/src/main/java/com/tech_symfony/movie_booking/system/config/api/SwaggerConfiguration.java b/src/main/java/com/tech_symfony/movie_booking/system/config/api/SwaggerConfiguration.java index ad1710e..0edd363 100644 --- a/src/main/java/com/tech_symfony/movie_booking/system/config/api/SwaggerConfiguration.java +++ b/src/main/java/com/tech_symfony/movie_booking/system/config/api/SwaggerConfiguration.java @@ -1,5 +1,9 @@ package com.tech_symfony.movie_booking.system.config.api; +import com.tech_symfony.movie_booking.system.exception.ErrorInfo; +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverters; +import io.swagger.v3.core.converter.ResolvedSchema; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; import io.swagger.v3.oas.annotations.info.Contact; @@ -7,7 +11,14 @@ import io.swagger.v3.oas.annotations.security.OAuthFlow; import io.swagger.v3.oas.annotations.security.OAuthFlows; import io.swagger.v3.oas.annotations.security.SecurityScheme; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.responses.ApiResponse; +import org.springdoc.core.customizers.OpenApiCustomizer; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.hateoas.MediaTypes; @SecurityScheme( name = "security_auth", @@ -27,28 +38,50 @@ description = "Open Api for movie booking app", title = "OpenApi movie booking ", version = "1.0" - ) + ), + servers = {@Server(url = "/", description = "Default Server URL")} ) public class SwaggerConfiguration { + @Bean + public OpenApiCustomizer openApiCustomizer() { + ResolvedSchema errResSchema = + ModelConverters.getInstance().resolveAsResolvedSchema(new AnnotatedType(ErrorInfo.class)); + Content content = + new Content().addMediaType(MediaTypes.HAL_JSON_VALUE, new MediaType().schema(errResSchema.schema)); + return openApi -> + openApi + .getPaths() + .values() + .forEach( + pathItem -> + pathItem + .readOperations() + .forEach( + operation -> + operation + .getResponses() + + .addApiResponse( + "403", + new ApiResponse() + .description("Access denied") + .content(content)) + .addApiResponse( + "500", + new ApiResponse() + .description("Server error") + .content(content)) + .addApiResponse( + "400", + new ApiResponse() + .description("Bad Request") + .content(content)) + .addApiResponse( + "404", + new ApiResponse() + .description("Resource Not Found") + .content(content)))); + } -// @Bean -// SpringDocConfiguration springDocConfiguration() { -// return new SpringDocConfiguration(); -// } -// -// @Bean -// SpringDocConfigProperties springDocConfigProperties() { -// return new SpringDocConfigProperties(); -// } -// -// @Bean -// ObjectMapperProvider objectMapperProvider(SpringDocConfigProperties springDocConfigProperties) { -// return new ObjectMapperProvider(springDocConfigProperties); -// } -// -// @Bean -// SpringDocUIConfiguration SpringDocUIConfiguration(Optional optionalSwaggerUiConfigProperties) { -// return new SpringDocUIConfiguration(optionalSwaggerUiConfigProperties); -// } } diff --git a/src/main/java/com/tech_symfony/movie_booking/system/config/auth/AuthServiceUtils.java b/src/main/java/com/tech_symfony/movie_booking/system/config/auth/AuthServiceUtils.java new file mode 100644 index 0000000..da86b4a --- /dev/null +++ b/src/main/java/com/tech_symfony/movie_booking/system/config/auth/AuthServiceUtils.java @@ -0,0 +1,16 @@ +package com.tech_symfony.movie_booking.system.config.auth; + +import com.tech_symfony.movie_booking.api.user.User; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.security.Principal; + +public class AuthServiceUtils { + +// public User getCurrentUser() { +// +// Principal principal = SecurityContextHolder.getContext().getAuthentication(); +// return principal.getName(); +// } + +} diff --git a/src/main/java/com/tech_symfony/movie_booking/system/config/auth/AuthorizationServerConfig.java b/src/main/java/com/tech_symfony/movie_booking/system/config/auth/AuthorizationServerConfig.java index f0356a7..f8f8fe1 100644 --- a/src/main/java/com/tech_symfony/movie_booking/system/config/auth/AuthorizationServerConfig.java +++ b/src/main/java/com/tech_symfony/movie_booking/system/config/auth/AuthorizationServerConfig.java @@ -46,7 +46,7 @@ public class AuthorizationServerConfig { @Bean public RegisteredClientRepository registeredClientRepository() { if (StringUtils.isEmpty(FRONTEND_URL)) { - FRONTEND_URL = BASE_URL + "/brower-callback"; + FRONTEND_URL = "http://localhost:3000" + "/brower-callback"; } RegisteredClient publicClient = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("public-client") diff --git a/src/main/java/com/tech_symfony/movie_booking/system/exception/ErrorInfo.java b/src/main/java/com/tech_symfony/movie_booking/system/exception/ErrorInfo.java new file mode 100644 index 0000000..5555bd9 --- /dev/null +++ b/src/main/java/com/tech_symfony/movie_booking/system/exception/ErrorInfo.java @@ -0,0 +1,29 @@ +package com.tech_symfony.movie_booking.system.exception; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +@Getter +public class ErrorInfo { + private final String className; + private final String timestamp; + private List errors; + + public ErrorInfo(Exception ex) { + this.className = ex.getClass().getName(); + this.errors = new ArrayList<>(); + HashMap error = new HashMap<>(); + error.put("exMessage", ex.getLocalizedMessage()); + this.errors.add(error); + this.timestamp = String.valueOf(System.currentTimeMillis()); + } + + public ErrorInfo(Exception ex, List errors) { + this.className = ex.getClass().getName(); + this.errors = errors; + this.timestamp = String.valueOf(System.currentTimeMillis()); + } +} diff --git a/src/main/java/com/tech_symfony/movie_booking/system/exception/GlobalExceptionHandler.java b/src/main/java/com/tech_symfony/movie_booking/system/exception/GlobalExceptionHandler.java index 15ed92b..ba89b8f 100644 --- a/src/main/java/com/tech_symfony/movie_booking/system/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/tech_symfony/movie_booking/system/exception/GlobalExceptionHandler.java @@ -1,18 +1,8 @@ package com.tech_symfony.movie_booking.system.exception; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.tech_symfony.movie_booking.api.role.exception.RoleInUseException; -import com.tech_symfony.movie_booking.api.role.exception.RoleNotFoundException; import jakarta.validation.ConstraintViolationException; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -// import org.springframework.samples.petclinic.rest.controller.BindingErrorsResponse; import org.springframework.security.access.AccessDeniedException; - -import org.springframework.validation.BindingResult; -import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -41,7 +31,7 @@ public class GlobalExceptionHandler { @ExceptionHandler({MethodArgumentNotValidException.class, ConstraintViolationException.class}) @ResponseStatus(code = BAD_REQUEST) @ResponseBody - public ResponseEntity handlerException(ConstraintViolationException e, WebRequest request) { + public ErrorInfo handlerException(ConstraintViolationException e, WebRequest request) { final List errors = new ArrayList<>(); e.getConstraintViolations().stream().forEach(fieldError -> { Map error = new HashMap<>(); @@ -49,36 +39,44 @@ public ResponseEntity handlerException(ConstraintViolationException e, WebReq error.put("message", fieldError.getMessage()); errors.add(error); }); - HttpStatus httpStatus = HttpStatus.BAD_REQUEST; - Map body = new HashMap<>(); - body.put("error", errors); - return new ResponseEntity<>(body, httpStatus); + + return new ErrorInfo(e, errors); } - @ExceptionHandler(RuntimeException.class) - public ResponseEntity exception(Exception e) { - ObjectMapper mapper = new ObjectMapper(); - ErrorInfo errorInfo = new ErrorInfo(e); - String respJSONstring = "{}"; - try { - respJSONstring = mapper.writeValueAsString(errorInfo); - } catch (JsonProcessingException e1) { - e1.printStackTrace(); - } - if (e instanceof AccessDeniedException) - return ResponseEntity.status(HttpStatus.FORBIDDEN).body(respJSONstring); + @ExceptionHandler(AccessDeniedException.class) + @ResponseBody + @ResponseStatus(HttpStatus.FORBIDDEN) + public ErrorInfo accessDenied(Exception e) { + return new ErrorInfo(e); + } - return ResponseEntity.badRequest().body(respJSONstring); + + @ExceptionHandler(RuntimeException.class) + @ResponseBody + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ErrorInfo exception(Exception e) { + return new ErrorInfo(e); } - private class ErrorInfo { - public final String className; - public final String exMessage; + // public ResponseEntity exception(Exception e) { +// ObjectMapper mapper = new ObjectMapper(); +// ErrorInfo errorInfo = new ErrorInfo(e); +// String respJSONstring = "{}"; +// try { +// respJSONstring = mapper.writeValueAsString(errorInfo); +// } catch (JsonProcessingException e1) { +// e1.printStackTrace(); +// } +// +// ResponseEntity.BodyBuilder responseWithStatus; +// if (e instanceof AccessDeniedException) { +// responseWithStatus = ResponseEntity.status(HttpStatus.NOT_FOUND); +// } else { +// responseWithStatus = ResponseEntity.status(HttpStatus.BAD_REQUEST); +// } +// +// return responseWithStatus.body(respJSONstring); +// } - public ErrorInfo(Exception ex) { - this.className = ex.getClass().getName(); - this.exMessage = ex.getLocalizedMessage(); - } - } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 855ff31..70db47a 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -6,7 +6,9 @@ server: #spring.jpa.show-sql: true springdoc: - paths-to-exclude: /profile/** + paths-to-exclude: + - /profile/** + - /tickets # url=/openapi-3: # 0: @@ -30,9 +32,9 @@ springdoc: spring: -# -# mvc: -# log-resolved-exception: false + # + # mvc: + # log-resolved-exception: false elasticsearch: username: elastic @@ -76,7 +78,7 @@ spring: logging: level: - org.springframework.security: trace + org.springframework.security: ${ DEBUG } jwt: key: diff --git a/src/test/java/com/tech_symfony/movie_booking/MovieBookingApplicationIT.java b/src/test/java/com/tech_symfony/movie_booking/MovieBookingApplicationIT.java new file mode 100644 index 0000000..34713d1 --- /dev/null +++ b/src/test/java/com/tech_symfony/movie_booking/MovieBookingApplicationIT.java @@ -0,0 +1,19 @@ +package com.tech_symfony.movie_booking; + +import com.tech_symfony.movie_booking.model.BaseIntegrationTest; +import org.junit.jupiter.api.Test; + + +class MovieBookingApplicationIT extends BaseIntegrationTest { + + @Test + void contextLoads() { + + } + + @Test + public void applicationContextTest() { + MovieBookingApplication.main(new String[]{}); + } + +} diff --git a/src/test/java/com/tech_symfony/movie_booking/MovieBookingApplicationTests.java b/src/test/java/com/tech_symfony/movie_booking/MovieBookingApplicationTests.java index 64fd246..5371a54 100644 --- a/src/test/java/com/tech_symfony/movie_booking/MovieBookingApplicationTests.java +++ b/src/test/java/com/tech_symfony/movie_booking/MovieBookingApplicationTests.java @@ -1,15 +1,9 @@ package com.tech_symfony.movie_booking; import com.tech_symfony.movie_booking.system.config.ESTestConfiguration; -import com.tech_symfony.movie_booking.system.container.ElasticTestContainer; -import org.junit.After; -import org.junit.Before; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; -import org.testcontainers.elasticsearch.ElasticsearchContainer; @SpringBootTest @Import(ESTestConfiguration.class) diff --git a/src/test/java/com/tech_symfony/movie_booking/api/CrashControllerIT.java b/src/test/java/com/tech_symfony/movie_booking/api/CrashControllerIT.java new file mode 100644 index 0000000..015159f --- /dev/null +++ b/src/test/java/com/tech_symfony/movie_booking/api/CrashControllerIT.java @@ -0,0 +1,32 @@ +package com.tech_symfony.movie_booking.api; + +import com.tech_symfony.movie_booking.model.BaseIntegrationTest; +import org.junit.jupiter.api.Test; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.resourceDetails; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.fromMethodCall; +import static org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.on; + +public class CrashControllerIT extends BaseIntegrationTest { + @Test + void testTriggerExceptionJson() throws Exception { + String uri = fromMethodCall(on(CrashController.class).triggerException()).build().toUriString(); + this.mockMvc.perform(get(uri)) + .andExpect(status().isInternalServerError()) + .andDo(document("error-example", + resourceDetails() + .description("Get error links."), + responseFields(subsectionWithPath("errors").description("The error message"), + subsectionWithPath("className").description("The path that caused the error"), + subsectionWithPath("timestamp").description("The timestamp of the error")) + )); + } + + +} diff --git a/src/test/java/com/tech_symfony/movie_booking/api/RootControllerIT.java b/src/test/java/com/tech_symfony/movie_booking/api/RootControllerIT.java new file mode 100644 index 0000000..4416b50 --- /dev/null +++ b/src/test/java/com/tech_symfony/movie_booking/api/RootControllerIT.java @@ -0,0 +1,46 @@ +package com.tech_symfony.movie_booking.api; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.tech_symfony.movie_booking.model.BaseIntegrationTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.resourceDetails; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.util.StringUtils.collectionToDelimitedString; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; + +public class RootControllerIT extends BaseIntegrationTest { + @Autowired + private ObjectMapper objectMapper; + + + @Test + public void indexExample() throws Exception { + this.mockMvc.perform(get("/")) + .andExpect(status().isOk()) + .andDo(document("index-example", + resourceDetails() + .description("Get all base links."), + links(linkWithRel("login").description("The login") + + , linkWithRel("movies").description("The movies resource") + + , linkWithRel("cinemas").description("The cinemas resource") + + , linkWithRel("showtimes").description("The showtimes resource")), + + responseFields(subsectionWithPath("_links").description("Links to other resources")), + responseHeaders(headerWithName("Content-Type").description("The Content-Type of the payload, e.g. `application/hal+json`")))); + } + + +} diff --git a/src/test/java/com/tech_symfony/movie_booking/api/bill/BillIT.java b/src/test/java/com/tech_symfony/movie_booking/api/bill/BillIT.java new file mode 100644 index 0000000..d8c7c19 --- /dev/null +++ b/src/test/java/com/tech_symfony/movie_booking/api/bill/BillIT.java @@ -0,0 +1,152 @@ +package com.tech_symfony.movie_booking.api.bill; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jayway.jsonpath.JsonPath; +import com.nimbusds.jose.shaded.gson.Gson; +import com.tech_symfony.movie_booking.api.bill.vnpay.VnpayService; +import com.tech_symfony.movie_booking.model.BaseIntegrationTest; +import org.json.JSONObject; +import org.junit.Assert; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.UUID; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.fromMethodCall; +import static org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.on; + +@WithMockUser(username = "admin@gmail.com", password = "test", authorities = "SAVE_BILL") +public class BillIT extends BaseIntegrationTest { + + @Autowired + private ObjectMapper objectMapper; + @MockBean + private VnpayService vnpayService; + private static UUID billID; + + @Test + @Order(1) + @Sql({"/showtime.sql", "/bill.sql"}) + void createBillSuccess() throws Exception { + + BillRequestDTO billRequestDTO = new BillRequestDTO(); + billRequestDTO.setSeatId(List.of(1, 2)); + billRequestDTO.setShowtimeId(1); + String url = fromMethodCall(on(BillController.class).create(billRequestDTO)).build().toUriString(); + + when(vnpayService.doPost(any(Bill.class))).thenReturn("vnpayUrl"); + MvcResult result = this.mockMvc.perform(post(url) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(billRequestDTO))) + .andExpect(jsonPath("$.status").value(BillStatus.IN_PROGRESS.toString())) + .andExpect(status().isCreated()) + .andExpect(header().string("Location", notNullValue())) + .andDo(document("create-bill", + requestFields( + fieldWithPath("showtimeId").description("The ID of the showtime"), + fieldWithPath("seatId").description("The list of seat IDs") + ), + responseFields( + fieldWithPath("id").description("The bill's ID"), + fieldWithPath("total").description("The total amount"), + fieldWithPath("status").description("The bill's status"), + fieldWithPath("paymentAt").description("The payment timestamp"), + fieldWithPath("transactionId").description("The transaction ID"), + subsectionWithPath("_links").description("Links to other resources") + ), + links( + linkWithRel("self").description("Link to this resource").optional(), + linkWithRel("bill").description("Link to this resource"), + linkWithRel("bills").description("Link to the bill collection"), + linkWithRel("pay").optional().description("Link to pay the bill if in progress") + ), + responseHeaders( + headerWithName("Location").description("The location of the created showtime") + ) + )).andReturn(); + String content = result.getResponse().getContentAsString(); + billID = UUID.fromString(JsonPath.read(content, "$.id")); + } + + @Test + @Order(2) + void payBillSuccess() throws Exception { + String url = fromMethodCall(on(BillController.class).pay(billID)).build().toUriString(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("vnp_TransactionNo", "12345"); + + when(vnpayService.verifyPay(any(Bill.class))).thenReturn(jsonObject); + this.mockMvc.perform(put(url)) + .andExpect(status().isOk()) + .andDo(print()) + .andExpect(jsonPath("$.transactionId").value("12345")) + .andExpect(jsonPath("$.status").value(BillStatus.HOLDING.toString())) + .andDo(document("pay-bill", + responseFields( + fieldWithPath("id").description("The bill's ID"), + fieldWithPath("total").description("The total amount"), + fieldWithPath("status").description("The bill's status"), + fieldWithPath("paymentAt").description("The payment timestamp"), + fieldWithPath("transactionId").description("The transaction ID"), + subsectionWithPath("_links").description("Links to other resources") + ), + links( + linkWithRel("self").description("Link to this resource").optional(), + linkWithRel("bill").description("Link to this resource"), + linkWithRel("bills").description("Link to the bill collection") + ) + )) + ; + + + } + + // + @Test + @Order(3) + void updateBillStatusSuccess() throws Exception { + String url = fromMethodCall(on(BillController.class).updateStatus(billID)).build().toUriString(); + this.mockMvc.perform(put(url)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.status").value(BillStatus.COMPLETED.toString())) + .andDo(document("complete-bill", + responseFields( + fieldWithPath("id").description("The bill's ID"), + fieldWithPath("total").description("The total amount"), + fieldWithPath("status").description("The bill's status"), + fieldWithPath("paymentAt").description("The payment timestamp"), + fieldWithPath("transactionId").description("The transaction ID"), + subsectionWithPath("_links").description("Links to other resources") + ), + links( + linkWithRel("self").description("Link to this resource").optional(), + linkWithRel("bill").description("Link to this resource"), + linkWithRel("bills").description("Link to the bill collection") + ) + )); + } + + +} diff --git a/src/test/java/com/tech_symfony/movie_booking/api/bill/BillModelAssemblerTest.java b/src/test/java/com/tech_symfony/movie_booking/api/bill/BillModelAssemblerTest.java new file mode 100644 index 0000000..cb24781 --- /dev/null +++ b/src/test/java/com/tech_symfony/movie_booking/api/bill/BillModelAssemblerTest.java @@ -0,0 +1,118 @@ +package com.tech_symfony.movie_booking.api.bill; + +import com.tech_symfony.movie_booking.model.BaseUnitTest; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.data.projection.ProjectionFactory; +import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; + +import java.time.LocalDateTime; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; + + +class BillModelAssemblerTest extends BaseUnitTest { + + @InjectMocks + private BillModelAssembler billModelAssembler; + @Mock + private ProjectionFactory projectionFactory; + @Mock + private RepositoryEntityLinks entityLinks; + private static Bill billInProgress; + private static Bill billNotInProgress; + private static BillInfoProjection billInfoProjection = new BillInfoProjection() { + @Override + public UUID getId() { + return null; + } + + @Override + public Double getTotal() { + return 0.0; + } + + @Override + public BillStatus getStatus() { + return null; + } + + @Override + public LocalDateTime getPaymentAt() { + return null; + } + + @Override + public String getTransactionId() { + return ""; + } + }; + + @BeforeAll + public static void setUp() { + billInProgress = new Bill(); + billInProgress.setId(UUID.randomUUID()); + billInProgress.setStatus(BillStatus.IN_PROGRESS); + billInProgress.setTotal(100.5); // Random total amount between 1 and 500 + billInProgress.setVnpayUrl("http://example.com/payment/" + UUID.randomUUID()); // Simulated payment URL + billInProgress.setPaymentAt(LocalDateTime.now().minusDays(ThreadLocalRandom.current().nextInt(1, 30))); // Payment date within the last 30 days + billInProgress.setTransactionId(UUID.randomUUID().toString()); // Random transaction ID + billNotInProgress = new Bill(); + billNotInProgress.setId(UUID.randomUUID()); + billNotInProgress.setStatus(BillStatus.HOLDING); + + + } + + + @Test + void toModel_ShouldIncludePayLink_ForBillsInProgress() { + + Link linkNewResource = WebMvcLinkBuilder.linkTo(Bill.class).slash(billInProgress.getId()).withSelfRel(); + Link linkCollectionResource = WebMvcLinkBuilder.linkTo(Bill.class).withRel("bills"); + + when(entityLinks.linkToItemResource(Bill.class, billInProgress.getId())).thenReturn(linkNewResource); + when(entityLinks.linkToCollectionResource(Bill.class)).thenReturn(linkCollectionResource); + when(projectionFactory.createProjection(BillInfoProjection.class, billInProgress)).thenReturn(billInfoProjection); + EntityModel billModel = billModelAssembler.toModel(billInProgress); + + assertNotNull(billModel); + assertTrue(billModel.getLinks().contains(linkNewResource)); + assertTrue(billModel.getLinks().contains(linkCollectionResource)); + assertTrue(billModel.getLinks().hasLink("pay")); + + + } + + @Test + void toModel_ShouldNotIncludePayLink_ForBillsNotInProgress() { + Link linkNewResource = WebMvcLinkBuilder.linkTo(Bill.class).slash(billNotInProgress.getId()).withSelfRel(); + Link linkCollectionResource = WebMvcLinkBuilder.linkTo(Bill.class).withRel("bills"); + + when(entityLinks.linkToItemResource(Bill.class, billNotInProgress.getId())).thenReturn(linkNewResource); + when(entityLinks.linkToCollectionResource(Bill.class)).thenReturn(linkCollectionResource); + when(projectionFactory.createProjection(BillInfoProjection.class, billNotInProgress)).thenReturn(billInfoProjection); + EntityModel billModel = billModelAssembler.toModel(billNotInProgress); + + assertNotNull(billModel); + assertTrue(billModel.getLinks().contains(linkNewResource)); + assertTrue(billModel.getLinks().contains(linkCollectionResource)); + assertThat(billModel.getLink("pay")).isNotPresent(); + } +} diff --git a/src/test/java/com/tech_symfony/movie_booking/api/bill/BillServiceTest.java b/src/test/java/com/tech_symfony/movie_booking/api/bill/BillServiceTest.java index 832fe78..ab998d6 100644 --- a/src/test/java/com/tech_symfony/movie_booking/api/bill/BillServiceTest.java +++ b/src/test/java/com/tech_symfony/movie_booking/api/bill/BillServiceTest.java @@ -7,20 +7,24 @@ import com.tech_symfony.movie_booking.api.seat_type.SeatType; import com.tech_symfony.movie_booking.api.showtime.Showtime; import com.tech_symfony.movie_booking.api.showtime.ShowtimeRepository; +import com.tech_symfony.movie_booking.api.ticket.Ticket; import com.tech_symfony.movie_booking.api.ticket.TicketRepository; import com.tech_symfony.movie_booking.api.user.User; import com.tech_symfony.movie_booking.api.user.UserRepository; import com.tech_symfony.movie_booking.model.BaseUnitTest; +import com.tech_symfony.movie_booking.system.exception.ForbidenMethodControllerException; import org.json.JSONObject; +import org.junit.jupiter.api.BeforeAll; 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; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.test.context.support.WithMockUser; -import java.time.LocalDateTime; import java.util.*; +import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; @@ -49,85 +53,232 @@ public class BillServiceTest extends BaseUnitTest { @InjectMocks private DefaultBillService billService; + private static BillRequestDTO billRequestDTO; + private static Showtime showtime; + private static List seatList; + private static User user; + private static UUID existingBillId; + private static Bill holdingBill; + private static Bill completedBill; + + @BeforeAll + public static void setUp() { + + user = new User(); + user.setEmail("user@example.com"); + Room room = new Room(); + room.setId(1); + showtime = new Showtime(); + showtime.setId(1); + showtime.setRoom(room); + + SeatType seatType = new SeatType(); + seatType.setPrice(100.0); + seatType.setId(1); + seatList = new ArrayList<>(); + Seat seat1 = new Seat(); + seat1.setId(2); + seat1.setType(seatType); + seat1.setRoom(room); + Seat seat2 = new Seat(); + seat2.setId(3); + seat2.setType(seatType); + seat1.setRoom(room); + seatList.add(seat1); + seatList.add(seat2); + + + billRequestDTO = new BillRequestDTO(); + billRequestDTO.setShowtimeId(showtime.getId()); + billRequestDTO.setSeatId(Arrays.asList(2, 3)); + + existingBillId = UUID.randomUUID(); + holdingBill = new Bill(); + holdingBill.setId(existingBillId); + holdingBill.setStatus(BillStatus.HOLDING); + + completedBill = new Bill(); + completedBill.setId(existingBillId); + completedBill.setStatus(BillStatus.COMPLETED); + + } + + @Test + void create_ShouldThrowResourceNotFoundException_WhenShowtimeDoesNotExist() { + + + when(showtimeRepository.findById(any())).thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, () -> billService.create(billRequestDTO, user.getEmail())); + } + + @Test + void create_ShouldThrowResourceNotFoundException_WhenSeatsDoNotExist() { + + + when(showtimeRepository.findById(showtime.getId())).thenReturn(Optional.of(showtime)); + when(seatRepository.findByIdInAndRoomId(anyList(), any(Integer.class))).thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, () -> billService.create(billRequestDTO, user.getEmail())); + } + + @Test + void create_ShouldThrowResourceNotFoundException_WhenSeatsAlreadyReserved() { + + + when(showtimeRepository.findById(billRequestDTO.getShowtimeId())).thenReturn(Optional.of(showtime)); + when(seatRepository.findByIdInAndRoomId(billRequestDTO.getSeatId(), showtime.getRoom().getId())).thenReturn(Optional.of(seatList)); + when(ticketRepository.findByShowtimeAndSeatIn(showtime, seatList)).thenReturn(Set.of(new Ticket())); + + Exception exception = assertThrows(ForbidenMethodControllerException.class, () -> billService.create(billRequestDTO, user.getEmail())); + assertEquals("Seat already reserved", exception.getMessage()); + verify(showtimeRepository, times(1)).findById(billRequestDTO.getShowtimeId()); + verify(seatRepository, times(1)).findByIdInAndRoomId(billRequestDTO.getSeatId(), showtime.getRoom().getId()); + verify(ticketRepository, times(1)).findByShowtimeAndSeatIn(showtime, seatList); + + } + + @Test + void create_ShouldThrowException_WhenUserNotFound() { + + + when(showtimeRepository.findById(billRequestDTO.getShowtimeId())).thenReturn(Optional.of(showtime)); + when(seatRepository.findByIdInAndRoomId(billRequestDTO.getSeatId(), showtime.getRoom().getId())).thenReturn(Optional.of(seatList)); + when(userRepository.findByEmail(user.getEmail())).thenReturn(Optional.empty()); + + Exception exception = assertThrows(UsernameNotFoundException.class, () -> billService.create(billRequestDTO, user.getEmail())); + + assertEquals("Conflict", exception.getMessage()); + } + + @Test + void create_ShouldReturnBill_WhenSuccessful() { + + + when(showtimeRepository.findById(billRequestDTO.getShowtimeId())).thenReturn(Optional.of(showtime)); + when(seatRepository.findByIdInAndRoomId(billRequestDTO.getSeatId(), showtime.getRoom().getId())).thenReturn(Optional.of(seatList)); + + when(userRepository.findByEmail(user.getEmail())).thenReturn(Optional.of(user)); + when(billRepository.save(any(Bill.class))).thenAnswer(invocation -> invocation.getArgument(0)); + when(vnpayService.doPost(any(Bill.class))).thenReturn("vnpayUrl"); + + Bill bill = billService.create(billRequestDTO, user.getEmail()); + + assertNotNull(bill); + assertEquals(200.0, bill.getTotal()); + assertEquals("vnpayUrl", bill.getVnpayUrl()); + verify(billRepository, times(1)).save(any(Bill.class)); + } + + @Test + void pay_ShouldThrowException_WhenUserNotFound() { + UUID billID = UUID.randomUUID(); + when(userRepository.findByEmail(anyString())).thenReturn(Optional.empty()); + + + Exception exception = assertThrows(UsernameNotFoundException.class, () -> { + billService.pay(billID, user.getEmail()); + }); + + assertEquals("Conflict", exception.getMessage()); + } + + @Test + void pay_ShouldThrowException_WhenBillNotFound() { + UUID billID = UUID.randomUUID(); + when(userRepository.findByEmail(anyString())).thenReturn(Optional.of(user)); + when(billRepository.findByIdAndUser(any(UUID.class), any(User.class))).thenReturn(Optional.empty()); + + Exception exception = assertThrows(ResourceNotFoundException.class, () -> { + billService.pay(billID, user.getEmail()); + }); + + assertEquals("Bill is not exits", exception.getMessage()); + } + + @Test + void pay_ShouldReturnUpdatedBill_WhenSuccessful() { + UUID billID = UUID.randomUUID(); + Bill bill = new Bill(); + bill.setId(billID); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("vnp_TransactionNo", "12345"); + + when(vnpayService.verifyPay(bill)).thenReturn(jsonObject); + when(userRepository.findByEmail(anyString())).thenReturn(Optional.of(user)); + when(billRepository.findByIdAndUser(any(UUID.class), any(User.class))).thenReturn(Optional.of(bill)); + when(billRepository.save(bill)).thenAnswer(invocation -> invocation.getArgument(0)); + + Bill updatedBill = billService.pay(billID, user.getEmail()); + + assertNotNull(updatedBill); + assertEquals(BillStatus.HOLDING, updatedBill.getStatus()); + assertNotNull(updatedBill.getPaymentAt()); + assertEquals("12345", updatedBill.getTransactionId()); + verify(billRepository, times(1)).save(bill); + } + + @Test + @WithMockUser(authorities = "SAVE_BILL") + public void testUpdateStatus_success() { + UUID billId = UUID.randomUUID(); + Bill bill = new Bill(); + bill.setStatus(BillStatus.HOLDING); + + when(billRepository.findById(billId)).thenReturn(Optional.of(bill)); + when(billRepository.save(bill)).thenReturn(bill); + + Bill updatedBill = billService.updateStatus(billId); + + assertNotNull(updatedBill); + assertEquals(BillStatus.COMPLETED, updatedBill.getStatus()); + verify(billRepository, times(1)).findById(billId); + verify(billRepository, times(1)).save(bill); + } + + @Test + @WithMockUser(authorities = "SAVE_BILL") + public void testUpdateStatus_billNotFound() { + UUID billId = UUID.randomUUID(); + + when(billRepository.findById(billId)).thenReturn(Optional.empty()); + + assertThrows(ResourceNotFoundException.class, () -> { + billService.updateStatus(billId); + }); + + verify(billRepository, times(1)).findById(billId); + verify(billRepository, times(0)).save(any(Bill.class)); + } + + @Test + @WithMockUser(authorities = "SAVE_BILL") + public void testUpdateStatus_billNotHolding() { + UUID billId = UUID.randomUUID(); + Bill bill = new Bill(); + bill.setStatus(BillStatus.IN_PROGRESS); + + when(billRepository.findById(billId)).thenReturn(Optional.of(bill)); + + assertThrows(ForbidenMethodControllerException.class, () -> { + billService.updateStatus(billId); + }); + + verify(billRepository, times(1)).findById(billId); + verify(billRepository, times(0)).save(any(Bill.class)); + } // @Test -// public void createBillSuccessfully() { -// // Given -// BillDTO billDTO = new BillDTO(); -// billDTO.setShowtimeId(123); -// billDTO.setSeatId(Arrays.asList(1, 2, 3)); -// -// Showtime showtime = new Showtime(); -// showtime.setId(123); -// Room room = new Room(); -// room.setId(456); -// showtime.setRoom(room); -// -// SeatType seatType = new SeatType(); -// seatType.setId(1); -// seatType.setName("VIP"); -// seatType.setPrice(100.0); -// Seat seat1 = new Seat(); -// seat1.setId(1); -// seat1.setRoom(room); -// seat1.setType(seatType); -// -// Seat seat2 = new Seat(); -// seat2.setId(2); -// seat2.setRoom(room); -// seat2.setType(seatType); -// -// List seatList = Arrays.asList(seat1, seat2); -// -// User user = new User(); -// user.setId(UUID.randomUUID()); -// -// Bill bill = new Bill(); -// bill.setId(UUID.randomUUID()); -// bill.setTotal(200.0); -// -// // When -// -// when(billRepository.save(any(Bill.class))).thenReturn(bill); -// when(ticketRepository.findByShowtimeAndSeat(anyInt(), anyInt())).thenReturn(new HashSet<>()); -// when(showtimeRepository.findById(anyInt())).thenReturn(Optional.of(showtime)); -// when(seatRepository.findAllById(anyList())).thenReturn(seatList); -// when(userRepository.findByEmail(anyString())).thenReturn(Optional.of(user)); -// when(vnpayService.doPost(any(Bill.class))).thenReturn("https://vnpay.vn"); -// -// Bill result = billService.create(billDTO, "test@example.com"); -// -// // Then -// assertNotNull(result); -// verify(billRepository, times(1)).save(any(Bill.class)); -// } -// -// @Test -// public void payBillSuccessfully() { -// // Given -// Bill bill = new Bill(); -// bill.setId(UUID.randomUUID()); -// bill.setTotal(100.0); -// bill.setStatus(BillStatus.HOLDING); -// bill.setPaymentAt(LocalDateTime.now()); -// bill.setTransactionId("123456"); -// -// JSONObject jsonObject = new JSONObject(); -// jsonObject.put("vnp_TransactionNo", "123456"); -// Optional optionalBill = Optional.of(bill); -// when(billRepository.findById(any(UUID.class))).thenReturn(optionalBill); -// when(billRepository.save(any(Bill.class))).thenReturn(bill); -// -// when(vnpayService.verifyPay(bill)).thenReturn(jsonObject); +// public void testUpdateStatus_unauthorized() { +// UUID billId = UUID.randomUUID(); // -// // When -// Bill result = billService.pay(bill.getId()); +// assertThrows(AccessDeniedException.class, () -> { +// billService.updateStatus(billId); +// }); // -// // Then -// assertNotNull(result); -// assertEquals("123456", result.getTransactionId()); -// verify(billRepository, times(1)).save(any(Bill.class)); +// verify(billRepository, times(0)).findById(any(UUID.class)); +// verify(billRepository, times(0)).save(any(Bill.class)); // } } diff --git a/src/test/java/com/tech_symfony/movie_booking/api/bill/BillTest.java b/src/test/java/com/tech_symfony/movie_booking/api/bill/BillTest.java new file mode 100644 index 0000000..fd7cc8d --- /dev/null +++ b/src/test/java/com/tech_symfony/movie_booking/api/bill/BillTest.java @@ -0,0 +1,152 @@ +package com.tech_symfony.movie_booking.api.bill; + +import com.tech_symfony.movie_booking.api.ticket.Ticket; +import com.tech_symfony.movie_booking.api.user.User; +import com.tech_symfony.movie_booking.model.BaseUnitTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +public class BillTest extends BaseUnitTest { + + private Bill bill; + private Validator validator; + + @BeforeEach + public void setUp() { + bill = new Bill(); + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator(); + bill.setUser(new User()); + } + + @Test + public void testCreateTimeInitialization() { + assertNull(bill.getCreateTime()); + } + + @Test + public void testPaymentAtInitialization() { + assertNull(bill.getPaymentAt()); + } + + @Test + public void testChangedPointValidation() { + bill.setChangedPoint(51); + Set> violations = validator.validate(bill); + assertFalse(violations.isEmpty()); + assertEquals("Changed point MUST be less than or equal to 18", violations.iterator().next().getMessage()); + + bill.setChangedPoint(-1); + violations = validator.validate(bill); + assertFalse(violations.isEmpty()); + assertEquals("Changed point MUST MUST be at least 0", violations.iterator().next().getMessage()); + } + + @Test + public void testTotalValidation() { + bill.setTotal(-1.0); + Set> violations = validator.validate(bill); + assertFalse(violations.isEmpty()); + assertEquals("Total must be positive", violations.iterator().next().getMessage()); + } + + @Test + public void testTransactionId() { + String transactionId = "12345"; + bill.setTransactionId(transactionId); + assertEquals(transactionId, bill.getTransactionId()); + } + + @Test + public void testCancelReasonValidation() { + bill.setCancelReason(null); + Set> violations = validator.validate(bill); + assertFalse(violations.isEmpty()); + assertEquals("Cancel reason must not be null", violations.iterator().next().getMessage()); + } + + @Test + public void testCancelDateInitialization() { + assertNull(bill.getCancelDate()); + } + + @Test + public void testTicketsInitialization() { + assertNull(bill.getTickets()); + } + + @Test + public void testAddTicket() { + Ticket ticket = new Ticket(); + bill.addTicket(ticket); + + assertNotNull(bill.getTickets()); + assertTrue(bill.getTickets().contains(ticket)); + } + + @Test + public void testUserValidation() { + bill.setUser(null); + Set> violations = validator.validate(bill); + assertFalse(violations.isEmpty()); + assertEquals("User must not be null", violations.iterator().next().getMessage()); + } + + @Test + public void testStatusInitialization() { + assertEquals(BillStatus.IN_PROGRESS, bill.getStatus()); + } + + @Test + public void testVnpayUrl() { + String vnpayUrl = "http://example.com"; + bill.setVnpayUrl(vnpayUrl); + assertEquals(vnpayUrl, bill.getVnpayUrl()); + } + + @Test + public void testBillSuccess() { + bill.setChangedPoint(10); + bill.setTotal(100.50); + bill.setTransactionId("TX123456789"); + bill.setCancelReason("Not applicable"); + bill.setCancelDate(LocalDateTime.now()); + + User user = new User(); + bill.setUser(user); + + bill.setStatus(BillStatus.COMPLETED); + + Ticket ticket1 = new Ticket(); + Ticket ticket2 = new Ticket(); + bill.addTicket(ticket1); + bill.addTicket(ticket2); + + Set> violations = validator.validate(bill); + assertTrue(violations.isEmpty()); + + assertEquals(10, bill.getChangedPoint()); + assertEquals(100.50, bill.getTotal()); + assertEquals("TX123456789", bill.getTransactionId()); + assertEquals("Not applicable", bill.getCancelReason()); + assertNotNull(bill.getCancelDate()); + assertEquals(user, bill.getUser()); + assertEquals(BillStatus.COMPLETED, bill.getStatus()); + assertNotNull(bill.getTickets()); + assertEquals(2, bill.getTickets().size()); + assertTrue(bill.getTickets().contains(ticket1)); + assertTrue(bill.getTickets().contains(ticket2)); + } +} + diff --git a/src/test/java/com/tech_symfony/movie_booking/api/bill/DeleteBillTaskTest.java b/src/test/java/com/tech_symfony/movie_booking/api/bill/DeleteBillTaskTest.java new file mode 100644 index 0000000..149b991 --- /dev/null +++ b/src/test/java/com/tech_symfony/movie_booking/api/bill/DeleteBillTaskTest.java @@ -0,0 +1,63 @@ +package com.tech_symfony.movie_booking.api.bill; + +import com.tech_symfony.movie_booking.model.BaseUnitTest; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Optional; +import java.util.UUID; + +import static org.mockito.Mockito.*; + +public class DeleteBillTaskTest extends BaseUnitTest { + @Mock + private BillRepository billRepository; + + private UUID billId; + private DeleteBillTask deleteBillTask; + + @BeforeEach + public void setUp() { + billId = UUID.randomUUID(); + deleteBillTask = new DeleteBillTask(billRepository, billId); + } + + @Test + public void testRun_billInProgress() { + Bill bill = new Bill(); + bill.setStatus(BillStatus.IN_PROGRESS); + + when(billRepository.findById(billId)).thenReturn(Optional.of(bill)); + + deleteBillTask.run(); + + verify(billRepository, times(1)).findById(billId); + verify(billRepository, times(1)).deleteById(billId); + } + + @Test + public void testRun_billNotInProgress() { + Bill bill = new Bill(); + bill.setStatus(BillStatus.COMPLETED); + + when(billRepository.findById(billId)).thenReturn(Optional.of(bill)); + + deleteBillTask.run(); + + verify(billRepository, times(1)).findById(billId); + verify(billRepository, times(0)).deleteById(billId); + } + + @Test + public void testRun_billNotFound() { + when(billRepository.findById(billId)).thenReturn(Optional.empty()); + + deleteBillTask.run(); + + verify(billRepository, times(1)).findById(billId); + verify(billRepository, times(0)).deleteById(billId); + } +} diff --git a/src/test/java/com/tech_symfony/movie_booking/api/showtime/ShowtimeIT.java b/src/test/java/com/tech_symfony/movie_booking/api/showtime/ShowtimeIT.java new file mode 100644 index 0000000..044c3f1 --- /dev/null +++ b/src/test/java/com/tech_symfony/movie_booking/api/showtime/ShowtimeIT.java @@ -0,0 +1,169 @@ +package com.tech_symfony.movie_booking.api.showtime; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.tech_symfony.movie_booking.api.movie.Movie; +import com.tech_symfony.movie_booking.api.room.Room; +import com.tech_symfony.movie_booking.model.BaseIntegrationTest; +import jakarta.transaction.Transactional; +import lombok.Getter; +import lombok.Setter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.hateoas.MediaTypes; +import org.springframework.hateoas.server.EntityLinks; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.web.servlet.ResultActions; + +import java.net.URI; +import java.sql.Date; +import java.sql.Time; +import java.time.LocalDateTime; +import java.time.LocalTime; + +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static org.hamcrest.Matchers.notNullValue; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@Transactional +@Sql("/showtime.sql") +public class ShowtimeIT extends BaseIntegrationTest { + @Autowired + private EntityLinks entityLinks; + @Autowired + private ObjectMapper objectMapper; + + private URI itemURL; + private URI collectionURL; + private URI movieItemURL; + private URI roomItemURL; + private Room room; + private Movie movie; + + + @Setter + @Getter + private class ShowtimeRequestDTO { + URI room; + URI movie; + int runningTime; + Time startTime; + LocalDateTime startDate; + + } + + @BeforeEach + public void setUp() { + itemURL = entityLinks.linkToItemResource(Showtime.class, 1).toUri(); + collectionURL = entityLinks.linkToCollectionResource(Showtime.class).toUri(); + movieItemURL = entityLinks.linkToItemResource(Movie.class, 1).toUri(); + roomItemURL = entityLinks.linkToItemResource(Room.class, 1).toUri(); + } + + @Test + public void testCreateShowtime() throws Exception { + + + ShowtimeRequestDTO showtimeRequestDTO = new ShowtimeRequestDTO(); + showtimeRequestDTO.setRunningTime(140); + showtimeRequestDTO.setRoom(roomItemURL); + showtimeRequestDTO.setMovie(movieItemURL); + showtimeRequestDTO.setStartDate(LocalDateTime.now().plusDays(1L)); + showtimeRequestDTO.setStartTime(Time.valueOf(LocalTime.parse("11:14:05"))); + ResultActions resultActions = this.mockMvc.perform(get(roomItemURL)) + .andExpect(status().isOk()); + this.mockMvc.perform(post(collectionURL) + .contentType(MediaTypes.HAL_JSON) + .content(objectMapper.writeValueAsString(showtimeRequestDTO))) + .andExpect(status().isCreated()) + .andExpect(header().string("Location", notNullValue())) + .andDo(document("create-showtime", + requestFields( + fieldWithPath("startDate").description("The start date of the showtime"), + fieldWithPath("startTime").description("The start time of the showtime"), + fieldWithPath("runningTime").description("The running time of the showtime"), + fieldWithPath("status").description("The status of the showtime").optional(), + fieldWithPath("movie").description("The title of the movie"), + fieldWithPath("room").description("The name of the room") + ), + responseFields( + fieldWithPath("startDate").description("The start date of the showtime"), + fieldWithPath("startTime").description("The start time of the showtime"), + fieldWithPath("runningTime").description("The running time of the showtime"), + fieldWithPath("status").description("The status of the showtime"), + fieldWithPath("movie").description("The title of the movie"), + fieldWithPath("room").description("The name of the room") + ), + responseHeaders( + headerWithName("Location").description("The location of the created showtime") + ))); + int a = 3; + } +// +// @Test +// public void testGetShowtime() throws Exception { +// this.mockMvc.perform(post(itemURL) +// .accept(MediaTypes.HAL_JSON)) +// .andExpect(status().isOk()) +// .andDo(document("get-showtime", +// responseFields( +// fieldWithPath("startDate").description("The start date of the showtime"), +// fieldWithPath("startTime").description("The start time of the showtime"), +// fieldWithPath("runningTime").description("The running time of the showtime"), +// fieldWithPath("status").description("The status of the showtime"), +// fieldWithPath("movie").description("The title of the movie"), +// fieldWithPath("room").description("The name of the room") +// ))); +// } +// +// @Test +// public void testGetCollectionShowtime() throws Exception { +// this.mockMvc.perform(post(itemURL) +// .accept(MediaTypes.HAL_JSON)) +// .andExpect(status().isOk()) +// .andDo(document("get-collection-showtime", +// responseFields( +// fieldWithPath("_embedded.showtimes").description("The start date of the showtime") +// ))); +// } +// +// @Test +// public void testUpdateShowtime() throws Exception { +// String showtimeJson = "{ \"startDate\": \"2023-07-14\", \"startTime\": \"16:00:00\", \"runningTime\": 130, \"status\": false, \"movie\": \"Updated Movie\", \"room\": \"Room 2\" }"; +// +// this.mockMvc.perform(put(itemURL) +// .contentType(MediaTypes.HAL_JSON) +// .content(showtimeJson)) +// .andExpect(status().isOk()) +// .andDo(document("update-showtime", +// requestFields( +// fieldWithPath("startDate").description("The start date of the showtime"), +// fieldWithPath("startTime").description("The start time of the showtime"), +// fieldWithPath("runningTime").description("The running time of the showtime"), +// fieldWithPath("status").description("The status of the showtime"), +// fieldWithPath("movie").description("The title of the movie"), +// fieldWithPath("room").description("The name of the room") +// ), +// responseFields( +// fieldWithPath("startDate").description("The start date of the showtime"), +// fieldWithPath("startTime").description("The start time of the showtime"), +// fieldWithPath("runningTime").description("The running time of the showtime"), +// fieldWithPath("status").description("The status of the showtime"), +// fieldWithPath("movie").description("The title of the movie"), +// fieldWithPath("room").description("The name of the room") +// ))); +// } +// +// @Test +// public void testDeleteShowtime() throws Exception { +// this.mockMvc.perform(delete(itemURL)) +// .andExpect(status().isNoContent()) +// .andDo(document("delete-showtime")); +// } + +} diff --git a/src/test/java/com/tech_symfony/movie_booking/intergration/RestdocsExampleIT.java b/src/test/java/com/tech_symfony/movie_booking/intergration/RestdocsExampleIT.java deleted file mode 100644 index e5c5365..0000000 --- a/src/test/java/com/tech_symfony/movie_booking/intergration/RestdocsExampleIT.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.tech_symfony.movie_booking.intergration; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.tech_symfony.movie_booking.MovieBookingApplication; -import com.tech_symfony.movie_booking.model.BaseIntegrationTest; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders; -import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.util.StringUtils.collectionToDelimitedString; -import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; - -@SpringBootTest(classes = MovieBookingApplication.class) -public class RestdocsExampleIT extends BaseIntegrationTest { - @Autowired - private ObjectMapper objectMapper; - - -// @Test -// public void indexExample() throws Exception { -// this.mockMvc.perform(get("/")) -// .andExpect(status().isOk()) -// .andDo(document("index-example", -// resourceDetails() -// .description("Get all base links."), -// links(linkWithRel("login").description("The login") -// -// , linkWithRel("movies").description("The movies resource") -// -// , linkWithRel("cinemas").description("The cinemas resource") -// -// , linkWithRel("showtimes").description("The showtimes resource")), -// -// responseFields(subsectionWithPath("_links").description("Links to other resources")), -// responseHeaders(headerWithName("Content-Type").description("The Content-Type of the payload, e.g. `application/hal+json`")))); -// } - - -} diff --git a/src/test/java/com/tech_symfony/movie_booking/model/BaseIntegrationTest.java b/src/test/java/com/tech_symfony/movie_booking/model/BaseIntegrationTest.java index 6aae409..43d3f7b 100644 --- a/src/test/java/com/tech_symfony/movie_booking/model/BaseIntegrationTest.java +++ b/src/test/java/com/tech_symfony/movie_booking/model/BaseIntegrationTest.java @@ -1,16 +1,27 @@ package com.tech_symfony.movie_booking.model; +import com.tech_symfony.movie_booking.system.config.ESTestConfiguration; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; import org.springframework.restdocs.RestDocumentationContextProvider; import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.restdocs.cli.CliDocumentation; import org.springframework.restdocs.http.HttpDocumentation; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; import org.springframework.restdocs.mockmvc.UriConfigurer; +import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -18,25 +29,16 @@ import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @AutoConfigureRestDocs -@ExtendWith({RestDocumentationExtension.class, SpringExtension.class}) +@ExtendWith({RestDocumentationExtension.class, SpringExtension.class, MockitoExtension.class}) @TestPropertySource(properties = "spring.jackson.serialization.indent_output=true") +@SpringBootTest +@Import(ESTestConfiguration.class) public abstract class BaseIntegrationTest { protected MockMvc mockMvc; - - // -// - protected static MockHttpServletRequestBuilder getAuthenticateHeader(MockHttpServletRequestBuilder request) { - return request; - -// request.header("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c="); -// -// return request; - } - @BeforeEach public void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) { UriConfigurer mockMvcRestDocumentationConfigurer = @@ -52,6 +54,7 @@ public void setUp(WebApplicationContext webApplicationContext, RestDocumentation mockMvcRestDocumentationConfigurer.and().snippets() .withDefaults(CliDocumentation.curlRequest(), HttpDocumentation.httpRequest(), HttpDocumentation.httpResponse()) ) + .alwaysDo(print()) .alwaysDo(document("{class-name}/{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()))) .build(); } diff --git a/src/test/java/com/tech_symfony/movie_booking/system/container/BaseTestContainer.java b/src/test/java/com/tech_symfony/movie_booking/system/container/BaseTestContainer.java index 48c7ba9..f932cf1 100644 --- a/src/test/java/com/tech_symfony/movie_booking/system/container/BaseTestContainer.java +++ b/src/test/java/com/tech_symfony/movie_booking/system/container/BaseTestContainer.java @@ -2,29 +2,61 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.testcontainers.containers.MySQLContainer; import org.testcontainers.elasticsearch.ElasticsearchContainer; import org.testcontainers.elasticsearch.ElasticsearchContainer; import org.testcontainers.junit.jupiter.*; +import static org.assertj.core.api.Assertions.assertThat; + @Testcontainers @ExtendWith(SpringExtension.class) public class BaseTestContainer { @Container - protected static ElasticsearchContainer elasticsearchContainer = new ElasticTestContainer(); + protected final static ElasticsearchContainer elasticsearchContainer = new ElasticTestContainer(); + @Container + private final static MySQLContainer mySQLContainer = new MySQLContainer<>("mysql:8") + .withDatabaseName("testcontainer") + .withUsername("user") + .withPassword("pass"); + + @DynamicPropertySource + public static void properties(DynamicPropertyRegistry registry) { + registry.add("spring.datasource.url", mySQLContainer::getJdbcUrl); + registry.add("spring.datasource.username", mySQLContainer::getUsername); + registry.add("spring.datasource.password", mySQLContainer::getPassword); + + } + + private static void openContainer() { + elasticsearchContainer.start(); + mySQLContainer.start(); + + } + + private static void closeContainer() { + mySQLContainer.stop(); + elasticsearchContainer.stop(); + + } @BeforeAll static void setUp() { - elasticsearchContainer.stop(); - elasticsearchContainer.start(); + closeContainer(); + openContainer(); } @AfterAll static void destroy() { - // runs after every class - elasticsearchContainer.stop(); + closeContainer(); + } + } diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml index b1e9617..95f5e13 100644 --- a/src/test/resources/application.yaml +++ b/src/test/resources/application.yaml @@ -2,11 +2,11 @@ server: port: 8080 spring: - application: - name: movie_booking datasource: url: jdbc:h2:mem:db_dev driver-class-name: org.h2.Driver + application: + name: movie_booking jpa: hibernate: ddl-auto: create-drop diff --git a/src/test/resources/bill.sql b/src/test/resources/bill.sql new file mode 100644 index 0000000..ae6cf82 --- /dev/null +++ b/src/test/resources/bill.sql @@ -0,0 +1,18 @@ +INSERT INTO `seat_types` (`id`, `name`, `price`) +VALUES (1, 'Normal', 50000), + (2, 'VIP', 70000), + (3, 'string', 0); +INSERT INTO `seats` (`id`, `row_name`, `row_index`, `status`, `room_id`, `seat_type_id`) +VALUES (1, 'A', 1, 1, 1, 1), + (2, 'A', 2, 1, 1, 1), + (3, 'A', 3, 1, 1, 1); +INSERT INTO `roles` (`id`, `name`) +VALUES (1, 'ROLE_ADMIN'); +INSERT INTO `users` (`id`, `email`, `password`, `date_of_birth`, `role_id`, `verify`, `create_date`, `full_name`, + `verify_account`, `verify_pass`, `gender`, `phone_number`, `avatar`, `provider_id`, + `provider`) +VALUES (random_uuid(), 'admin@gmail.com', + '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', NULL, 1, '1', '2023-11-23 14:19:45.829821', + 'admin', NULL, NULL, 'UNKNOWN', NULL, + 'https://cdn.discordapp.com/attachments/1168144426141499412/1169264779769155594/c6e56503cfdd87da299f72dc416023d4.jpg?ex=6554c5af&is=654250af&hm=440ebc745e441103a1824a80edb22f2d1c371743e8d7a4d413dbbc62eae31554&', + NULL, 'LOCAL'); diff --git a/src/test/resources/db/data.sql b/src/test/resources/db/data.sql new file mode 100644 index 0000000..97983cf --- /dev/null +++ b/src/test/resources/db/data.sql @@ -0,0 +1,1540 @@ +-- phpMyAdmin SQL Dump +-- version 5.2.1 +-- https://www.phpmyadmin.net/ +-- +-- Host: localhost:3306 +-- Generation Time: Jul 10, 2024 at 10:34 PM +-- Server version: 10.6.17-MariaDB +-- PHP Version: 8.1.28 + +SET FOREIGN_KEY_CHECKS=0; +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +START TRANSACTION; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; + +-- +-- Database: `db_dev` +-- +CREATE DATABASE IF NOT EXISTS `db_dev` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; +USE `db_dev`; + +-- +-- Dumping data for table `bills` +-- + +INSERT INTO `bills` (`id`, `payment_at`, `total`, `user_id`, `transaction_id`, `change_point`, `cancel_reason`, `status`, `create_time`, `cancel_date`) VALUES + (0x031cf16bfc944e7590bbe5e04926ec3e, NULL, 50000, 0x49c64e33d27338efa68c23e1aa16e499, 'b386b2cc-c407-3a63-b09f-c8f6230d9ac8', 0, NULL, 1, '2023-11-10 00:00:00.000000', NULL), + (0x04e6d2cb1a8048448d0c484057e05d63, NULL, 150000, 0x49c64e33d27338efa68c23e1aa16e499, '7c9d950b-4de5-36fc-a79e-fe1f233e60f8', 0, '\"Không coi nữa.\"', 3, '2023-10-27 00:00:00.000000', '2023-10-27 01:34:31.000000'), + (0x0d13aba8cca148aebde5dcb250644705, NULL, 150000, 0x49c64e33d27338efa68c23e1aa16e499, 'cf08b8ab-b73b-3536-881b-c78da79fafba', 0, NULL, 1, '2023-11-22 00:00:00.000000', NULL), + (0x442b8ee77f6d48ae866617a41b9ac882, '2023-11-29 21:57:20.833606', 100000, 0x49c64e33d27338efa68c23e1aa16e499, 'f4a724dd-5d85-33a1-a2cb-fc809b367c1c', 0, NULL, 2, '2023-11-29 00:00:00.000000', NULL), + (0x516cdf92447d46d2b2f713c67985233f, NULL, 200000, 0x49c64e33d27338efa68c23e1aa16e499, '3a30cb3c-7441-3f02-8012-43703cf1880d', 0, NULL, 1, '2023-11-14 00:00:00.000000', NULL), + (0x53db1ce614924807980804a3dc7765c5, '2024-06-01 16:19:33.849659', 100000, 0x76ea668f6c6b33bd8a9925f7eac2becf, '14439427', 0, '', 1, '2024-06-01 15:59:25.237000', NULL), + (0x577cc36a800e42fb956fd191b5cd6d94, '2023-10-28 00:00:00.000000', 190000, 0x49c64e33d27338efa68c23e1aa16e499, '15263ded-d328-3a72-8a05-b3f1f410bdde', 0, NULL, 2, '2023-10-28 00:00:00.000000', NULL), + (0x5e5c2ddb26c5474e86855be13f401599, NULL, 50000, 0xe33ffe0f51753ab0997167196c3fa9dc, '9821cd56-38a5-38f8-b6f6-3a40db5e8744', 0, NULL, 1, '2023-11-09 00:00:00.000000', NULL), + (0x6ad7e232c24249a8b658d15dcbf2f353, '2023-11-29 22:29:36.493181', 100000, 0x49c64e33d27338efa68c23e1aa16e499, 'dfb743b5-a8db-3ab7-9aa2-4b2ecb4a9835', 0, NULL, 2, '2023-11-29 00:00:00.000000', NULL), + (0x78598fef81524cbd9bf821620ce42a45, '2023-11-02 19:47:14.000000', 30000, 0x49c64e33d27338efa68c23e1aa16e499, '679c48de-1c32-3485-8a33-f828cbb32476', 20, NULL, 2, '2023-11-02 00:00:00.000000', NULL), + (0x79e2ac2bd0c14ae0878f5e558d782d3b, '2023-10-28 00:00:00.000000', 210000, 0x49c64e33d27338efa68c23e1aa16e499, 'c76bb79b-e360-32df-aace-15f907667ff2', 0, NULL, 2, '2023-10-28 00:00:00.000000', NULL), + (0xb76524a502bd4c51983286e933863f57, '2023-11-30 07:53:31.050337', 210000, 0x76ea668f6c6b33bd8a9925f7eac2becf, '2b4ffd73-00fc-3996-af24-b4be0dc0a275', 0, NULL, 2, '2023-11-30 00:00:00.000000', NULL), + (0xc86a74f8d7f24afb9675bc306c64e365, '2023-11-29 10:41:06.333967', 250000, 0x49c64e33d27338efa68c23e1aa16e499, 'ded00f7f-6fc6-3fbd-89e7-f8b4024cc3a1', 0, NULL, 2, '2023-11-29 00:00:00.000000', NULL), + (0xc9b50b86fae24e9e9811baa2e968d983, NULL, 120000, 0x49c64e33d27338efa68c23e1aa16e499, '9bdaf3aa-53e8-3dc8-b9f8-e4f3ab2f9179', 0, NULL, 1, '2023-11-14 00:00:00.000000', NULL), + (0xd464eb75b912498e809905a9f48ce78b, NULL, 250000, 0x76ea668f6c6b33bd8a9925f7eac2becf, '96da35e2-017f-310c-b0d3-314727c76a0b', 0, NULL, 1, '2023-11-23 00:00:00.000000', NULL), + (0xe772c5cc982d45a2a3d80e0a7e735fdf, NULL, 200000, 0x76ea668f6c6b33bd8a9925f7eac2becf, 'ad7c039a-a6b4-3597-a6dc-c19385ee03f4', 0, NULL, 1, '2023-11-23 00:00:00.000000', NULL); + +-- +-- Dumping data for table `bill_status` +-- + +INSERT INTO `bill_status` (`id`, `name`) VALUES + (1, 'Unpaid'), + (2, 'Paid'), + (3, 'Cancel'); + +-- +-- Dumping data for table `cinemas` +-- + +INSERT INTO `cinemas` (`id`, `address`, `city`, `district`, `name`, `description`, `phone_number`, `status`) VALUES + (1, '466', 'HCM', '122', 'Galaxy Trung Chanh', 'Hệ thống rạp The Cinema được thiết kế sang trọng và tiện nghi. Khách hàng bước vào không gian rạp sẽ cảm nhận ngay sự thoải mái và tiện lợi từ việc sắp xếp ghế ngồi theo lối kiến trúc hiện đại, tạo nên không gian xem phim tuyệt vời. Mỗi phòng chiếu được trang bị hệ thống âm thanh vòm và màn hình siêu nét, mang đến trải nghiệm hình ảnh và âm thanh sống động, hấp dẫn.', '091222', 'OPENING'), + (2, '116 Nguyễn Du, Quận 1, Tp.HCM', 'Tp.HCM', 'Quận 1', 'Galaxy Nguyễn Du', 'Hệ thống rạp The Cinema được thiết kế sang trọng và tiện nghi. Khách hàng bước vào không gian rạp sẽ cảm nhận ngay sự thoải mái và tiện lợi từ việc sắp xếp ghế ngồi theo lối kiến trúc hiện đại, tạo nên không gian xem phim tuyệt vời. Mỗi phòng chiếu được trang bị hệ thống âm thanh vòm và màn hình siêu nét, mang đến trải nghiệm hình ảnh và âm thanh sống động, hấp dẫn.', '09133333333', 'OPENING'), + (3, '246 Nguyễn Hồng Đào, Q.TB, Tp.HCM', 'Tp.HCM', 'Quận Tân Bình', 'Galaxy Tân Bình', 'Hệ thống rạp The Cinema được thiết kế sang trọng và tiện nghi. Khách hàng bước vào không gian rạp sẽ cảm nhận ngay sự thoải mái và tiện lợi từ việc sắp xếp ghế ngồi theo lối kiến trúc hiện đại, tạo nên không gian xem phim tuyệt vời. Mỗi phòng chiếu được trang bị hệ thống âm thanh vòm và màn hình siêu nét, mang đến trải nghiệm hình ảnh và âm thanh sống động, hấp dẫn.', '09122222222', 'OPENING'), + (4, '718bis Kinh Dương Vương, Q6, TpHCM.', 'Tp.HCM', 'Quận 6', 'Galaxy Kinh Dương Vương', NULL, '09122222222', 'CLOSED'), + (5, 'Lầu 3, TTTM CoopMart Foodcosa số 304A, Quang Trung, P.11, Q. Gò Vấp, Tp.HCM', 'Tp.HCM', 'Quận Gò vấp', 'Galaxy Quang Trung', NULL, '09122222222', 'CLOSED'), + (6, 'Lầu 1, TTTM Sense City 26A Trần Quốc Tuấn, Phường 4, TP. Bến Tre', 'Tp.Bến Tre', 'Phường 4', 'Galaxy Bến Tre', NULL, '09122222222', 'CLOSED'), + (7, 'Tầng 6, TTTM Mipec Long Biên, Số 2, Phố Long Biên 2, Ngọc Lâm, Long Biên, Hà Nội', 'Tp.Hà Nội', 'Long Biên', 'Galaxy Mipec Long Biên', NULL, '09122222222', 'CLOSED'), + (8, 'Tầng 3, TTTM Coop Mart, 478 Điện Biên Phủ, Quận Thanh Khê, Đà Nẵng', 'Tp.Đà Nẵng', 'Thanh Khê', 'Galaxy Đà Nẵng', NULL, '09122222222', 'CLOSED'), + (9, 'Lầu 2, TTTM Sense City, số 9, Trần Hưng Đạo, P.5, Tp. Cà Mau', 'Tp.Cà Mau', 'Phường 5', 'Galaxy Cà Mau', NULL, '09122222222', 'CLOSED'), + (10, 'TTVH Quận 12, Số 09 Quốc Lộ 22, P. Trung Mỹ Tây, Quận 12', 'Tp.HCM', 'Quận 12', 'Galaxy Trung Chánh', NULL, '09122222222', 'CLOSED'), + (11, 'Lầu 2, TTTM Coopmart, số 1362 Huỳnh Tấn Phát, khu phố 1, Phường Phú Mỹ, Quận 7, Tp.Hồ Chí Minh, Việt Nam.', 'Tp.HCM', 'Quận 7', 'Galaxy Huỳnh Tấn Phát', NULL, '09122222222', 'CLOSED'), + (12, 'Lầu 5, Trung tâm Giải Trí City HUB – số 1 Lê Hồng Phong, thành phố Vinh', 'Tp.Vinh', 'Null', 'Galaxy Vinh', NULL, '09122222222', 'CLOSED'), + (13, 'Lầu 7, TTTM Nguyễn Kim - Sài Gòn Mall, số 104 Lương Khánh Thiện.', 'Tp.Hải Phòng', 'Null', 'Galaxy Hải Phòng', NULL, '09122222222', 'CLOSED'), + (14, '119B Nguyễn Văn Quá, Phường Đông Hưng Thuận, Quận 12', 'Tp.HCM', 'Quận 12', 'Galaxy Nguyễn Văn Quá', NULL, '09122222222', 'CLOSED'), + (15, 'Tầng trệt, TTTM Coop Mart Buôn Ma Thuột, số 71 Nguyễn Tất Thành, Phường Tân An, Tp. Buôn Ma Thuột, Tỉnh Đắk Lắk, Việt Nam', 'Tp.Buôn Ma Thuộc', 'Phường Tân An', 'Galaxy Buôn Ma Thuột', NULL, '09122222222', 'CLOSED'), + (16, 'Tầng 1, TTTM Nguyễn Kim, số 01 Trần Hưng Đạo, Phường Mỹ Bình, Thành phố Long Xuyên', 'Tp.Long Xuyên', 'Phường Mỹ Đình', 'Galaxy Long Xuyên', NULL, '09122222222', 'CLOSED'), + (17, 'Tầng trệt, TTTM Co.opXtra Linh Trung, số 934 Quốc Lộ 1A, Phường Linh Trung, Quận Thủ Đức, Thành phố Hồ Chí Minh, Việt Nam', 'Tp.HCM', 'Quanan Thủ Đức', 'Galaxy Co.opXtra Linh Trung', NULL, '09122222222', 'CLOSED'), + (18, 'Tầng 3, Trung Tâm Thương Mại Nha Trang Center - 20 Trần Phú, Nha Trang, Khánh Hòa', 'Tp.Nha Trang', 'Null', 'Galaxy Nha Trang Center', NULL, '09122222222', 'CLOSED'), + (19, 'Tầng 3 - Co.opMart TTTM Thắng Lợi - Số 2 Trường Chinh, Tây Thạnh, Tân Phú, Thành phố Hồ Chí Minh', 'Tp.HCM', 'Quận Tân Phú', 'Galaxy Trường Chinh', NULL, '09122222222', 'CLOSED'); + +-- +-- Dumping data for table `cinemas_seq` +-- + +INSERT INTO `cinemas_seq` (`next_val`) VALUES + (1); + +-- +-- Dumping data for table `customer_order_seq` +-- + +INSERT INTO `customer_order_seq` (`next_val`) VALUES + (1); + +-- +-- Dumping data for table `movies` +-- + +INSERT INTO `movies` (`id`, `code`, `caster`, `description`, `director`, `language`, `name`, `sub_name`, `number_of_ratings`, `poster`, `rated`, `release_date`, `running_time`, `slug`, `trailer`, `end_date`, `horizontal_poster`, `sum_of_ratings`) VALUES + (1, 'mv-0001', 'Greta Lee, Teo Yoo', 'Muôn Kiếp Nhân Duyên xoay quanh hai nhân vật chính - Nora (Greta Lee) và Hae Sung (Teo Yoo). Tình bạn thân thiết của họ bị chia cắt khi Nora theo gia đình di cư khỏi Hàn Quốc vào năm 12 tuổi. 20 năm sau, như một mối duyên tiền định, họ gặp lại nhau tại Mỹ', 'Celine', 'Tiếng Anh', 'Past Lives', 'Muôn Kiếp Nhân Duyên', 3, 'https://cdn.discordapp.com/attachments/1159668660340789259/1169947106191093840/both-water.png?ex=65574126&is=6544cc26&hm=033c5de326692af06130dcc0e32cc684a441d99a98eecdc1c8e90cc028c27727&', 13, '2023-10-10', 106, 'past-lives', 'https://www.youtube.com/watch?v=lBdLBY249Do', '2023-11-25', 'https://cdn.discordapp.com/attachments/1159668660340789259/1170792832735387688/2-ga-ran.png?ex=655a54cb&is=6547dfcb&hm=9a0fcbd579af5a185b42278b807c3c3658a38c89ec598febf2d880daae1eb599&', 25), + (2, 'mv-0002', 'Patience Munchenbach, Guillaume Canet, Laetitia Dosch', 'Giữa một đợt nắng nóng bất thường, thì mây mưa giống như làn gió xua tan oi bức. Nhưng những đám mây kỳ lạ trên bầu trời nước Pháp lại đem tới cho con người sự huỷ diệt và chết chóc. Hàng loạt trận mưa axit đổ xuống, đốt cháy mạng sống của vạn vật dưới mặ', 'Just Philippot', 'Tiếng Anh', 'Acide', 'THẢM HỌA MƯA AXIT', 1, 'https://cdn.galaxycine.vn/media/2023/9/21/500x750_1695284002644.jpg', 13, '2024-03-16', 101, 'acide', 'https://www.youtube.com/watch?v=SdXxuuJmXm8', '2024-04-27', 'https://cdn.discordapp.com/attachments/1159668660340789259/1176092840787460186/dev.jpg?ex=656d9ccf&is=655b27cf&hm=d05be04a9afe9cea11e7a2104dd4ca46f184f1cbc5640454ce93aab4dd6ff022&', 10), + (3, 'mv-0003', 'Jin Seon-Kyu, Yoo Hae Jin, Kim Hee Seon', 'Chuyện phim xoay quanh nhân vật Chi-ho (Yoo Hae-jin) - nhà nghiên cứu bim bim với khả năng nếm vị xuất chúng, nhưng lại ngờ nghệch với mọi thứ xung quanh. Chi-ho là một người cực kỳ hướng nội, thích ở một mình và sống như một cái máy được lập trình sẵn mà', 'Lee Han', 'Tiếng Anh', 'Honey Sweet', 'NHÂN DUYÊN TIỀN ĐÌNH', 3, 'https://cdn.galaxycine.vn/media/2023/9/19/500x750_1695095394242.jpg', 13, '2023-11-10', 119, 'honey-sweet', 'https://www.youtube.com/watch?v=k6S4DE8P7mY', '2023-12-30', NULL, 11), + (4, 'mv-0004', 'Kristen Bell, Mckenna Grace, James Marsden', 'Một mảnh thiên thạch kỳ diệu đã rơi xuống Thành Phố Phiêu Lưu, mang đến siêu năng lực cho những chú cún PAW Patrol và biến chúng thành Những Chú Cún Quyền Năng. Phim mới PAW Patrol: Phim Siêu Đẳng ra m', 'Cal Brunker', 'Tiếng An', 'PAW Patrol: The Mighty Movie', 'PAW PATROL: PHIM SIÊU ĐẲNG', 5, 'https://cdn.discordapp.com/attachments/1157346998136090685/1179937231541121135/VerticalPoster06.png?ex=657b992e&is=6569242e&hm=f20b348160c498dc6d02440a5df0e8919a95c63a91dae482c631c48a5ce3ef1f&', 13, '2023-11-27', 93, 'paw-patrol-the-mighty-movie', 'https://www.youtube.com/watch?v=O_zaLcbqGtI', '2024-02-14', 'https://cdn.discordapp.com/attachments/1159668660340789259/1178269407802298408/4069621.jpg?ex=657587e6&is=656312e6&hm=db883b620f6f1700b739008ee1741cdd839f5d7e9ada1ff4986375bdc2dc225e&', 12), + (5, 'mv-0005', 'Minh Hoàng', 'Minh Hoàng: Điểm Sáng Trên Dòng Sóng Kỷ Nguyên Công Nghệ là một bộ phim tài liệu chân thực và đầy cảm hứng, khám phá cuộc đời đầy sóng gió của một lập trình viên tài năng mang tên Minh Hoàng. Phim dự kiến sẽ đưa người xem vào cuộc hành trình đầy thách thức của Minh Hoàng từ khi còn trẻ đến hình ảnh một nhà lập trình xuất sắc và sáng tạo.\n\nTrải qua những cuộc phỏng vấn chân thành và những cảnh quay tuyệt đẹp, bộ phim này sẽ khám phá sự khởi đầu của Minh Hoàng, từ việc bắt đầu học lập trình ở tuổi 12 tới những áp lực và khó khăn trong việc xây dựng sự nghiệp. Người xem sẽ thấy rằng Minh Hoàng đã phải đối mặt với những thử thách ngoạn mục, như thất bại trong các dự án quan trọng, căng thẳng tinh thần và áp lực của ngành công nghiệp công nghệ thông tin đầy cạnh tranh.\n\nTuy nhiên, bằng sự kiên trì và đam mê không ngừng, Minh Hoàng đã trở thành một lập trình viên nổi tiếng và tạo ra những ứng dụng và dự án ấn tượng. Cuộc hành trình này không chỉ đặt ra câu hỏi về sự hy sinh và tận tâm trong công việc, mà còn là câu chuyện về khát vọng, sự đổi mới và sức mạnh của kiến thức trong thời đại số hóa.\n\nMinh Hoàng: Điểm Sáng Trên Dòng Sóng Kỷ Nguyên Công Nghệ sẽ đưa người xem vào thế giới phức tạp và đầy cảm hứng của một lập trình viên tài năng, tạo nên cơ hội để họ hiểu thêm về cuộc đời và tinh thần lập trình viên xuất sắc. Phim không chỉ là một bức tranh về cá nhân, mà còn là cả một tượng đài cho sự khao khát và khả năng đổi đời của bất kỳ ai dám mơ ước và làm việc hết mình trong lĩnh vực công nghệ thông tin.', 'Thành Đạt', 'TIếng Em', 'Minh Hoàng đây rồi', 'Minh Hoang is here', 1, 'https://cdn.discordapp.com/attachments/1159668660340789259/1170768260313133167/255271622_957908704808243_8030076711602587439_n.jpg?ex=6575ed69&is=65637869&hm=4e259c85a4c685035526a302ae89d3e9b12a3ed5af3fbb8273b93e35cf02f596&', 18, '2023-12-06', 118, 'minh-hoang-ay-roi', 'https://www.youtube.com/watch?v=XC3ftnZ1WYk', '2024-01-06', 'https://cdn.discordapp.com/attachments/1159668660340789259/1179820926154575932/4081103.jpg?ex=657b2cdc&is=6568b7dc&hm=3cd1450f928ea23baaa2d962d2da052f3a0e1fba9a1c56aea9a852a536587894&', 1), + (6, 'mv-0006', ' Jason Statham, Sylvester Stallone, Megan Fox', 'Biệt Đội Đánh Thuê 4 (tựa gốc: Expend4ables) tiếp tục theo chân biệt đội đặc biệt này thực hiện các nhiệm vụ mới, lần này là ngăn chặn ông trùm khủng bố Suarto, với âm mưu buôn lậu vũ khí hạt nhân và kích động chiến tranh giữa 2 phe Nga và Mỹ.', 'Scott Waughs', 'Tiếng Anh', 'Expend4ables', 'Biệt Đội Đánh Thuê 4', 2, 'https://cdn.galaxycine.vn/media/2023/9/18/500x750_1695010127758.jpg', 13, '2023-11-20', 103, 'expend4ables', 'https://www.youtube.com/watch?v=DhlaBO-SwVE', '2024-01-02', 'https://cdn.discordapp.com/attachments/1159668660340789259/1179774559856099368/HorizontalPoster.png?ex=657b01ae&is=65688cae&hm=137be25ce67a9dec90e89b84b6921adea39da6f22caf269cf8c360a0d29c19f0&', 6), + (7, 'mv-0007', 'Josh O\'Connor, Mike Faist, Zendaya', 'Theo chân ba tay vợt quen biết nhau khi còn là thanh thiếu niên đến khi họ thi đấu trong một giải đấu quần vợt để trở thành người chiến thắng giải Grand Slam nổi tiếng thế giới, đồng thời khơi lại những kỳ phùng địch thủ cũ trong và ngoài sân đấu.', 'Luca Guadagnino', 'Tiếng Anh', 'Challengers', 'NHỮNG KẺ THÁCH ĐẤU', 2, 'https://cdn.galaxycine.vn/media/2023/9/18/500x750_1695010127758.jpg', 13, '2024-01-24', 120, 'challengers', 'https://www.youtube.com/watch?v=eZtOHAkFxgI', '2024-02-05', NULL, 8), +(8, 'mv-0008', 'Georgia Eyers, Dan Ewing, Tim Pocock', 'Sau một biến cố đau thương, Lara liên tục gặp phải những ảo giác đầy kinh hãi và dần mất kiểm soát tinh thần. Những vũ điệu cuồng loạn, sự xuất hiện của quỷ dữ khiến chồng cô đặt niềm tin cứu rỗi linh hồn của vợ vào một nhóm trừ tà. Niềm tin tôn giáo sẽ c', 'Georgia Eyers, Dan Ewing, Tim Pocock', 'Tiếng Anh', 'Godless', 'VŨ ĐIỆU QUỶ DỮ', 0, 'https://cdn.galaxycine.vn/media/2023/9/19/500x750_1695094850730.jpg', 13, '2023-09-28', 91, 'godless', '', '2023-10-20', NULL, 0); + +-- +-- Dumping data for table `movies_images` +-- + +INSERT INTO `movies_images` (`id`, `extension`, `path`, `movie_id`) VALUES +(1, 'jpg', 'https://media.lottecinemavn.com/Media/MovieFile/MovieImg/202310/11268_105_100003.jpg', 5), +(2, 'jpg', 'https://media.lottecinemavn.com/Media/MovieFile/MovieImg/202310/11268_105_100002.jpg', 5), +(27, 'png', 'https://cdn.discordapp.com/attachments/1159668660340789259/1162014363264241785/chips-removebg-preview.png?ex=653a6535&is=6527f035&hm=2233f8fe7cbcfebc547f598ac966fbb6049c37670e2a765a01b0df278e9baaa9&', 5), +(28, 'png', 'https://cdn.discordapp.com/attachments/1159668660340789259/1170797108794773635/6-nuggets.png?ex=655a58c7&is=6547e3c7&hm=1300493c54a63ce99d63c7921e2c040bcb3f44620c764f2a60a64ec8fcd02afd&', 1), +(29, 'png', 'https://cdn.discordapp.com/attachments/1159668660340789259/1170803129068044408/both-water.png?ex=655a5e62&is=6547e962&hm=cab20d4f38230cd1e3d15cc5d325e461a3d1728619d59d504c83d4a15cfe1181&', 5), +(30, 'png', 'https://cdn.discordapp.com/attachments/1159668660340789259/1170896724773044234/mcchicken-deluxe.png?ex=655ab58d&is=6548408d&hm=1ebd63eb051031a8d0fa31fefd6745fc7323eaa26759cf26f04153fd6e15219c&', 5), +(38, 'webp', 'https://cdn.discordapp.com/attachments/1157347026003050566/1181084806642151504/Anatomy-of-an-API-request-1-1.webp?ex=657fc5f1&is=656d50f1&hm=b96d01ccc654c0ba5c7c7129f8f3021d7aa3b630a18062d07af145ff8b06aa33&', 5); + +-- +-- Dumping data for table `movies_seq` +-- + +INSERT INTO `movies_seq` (`next_val`) VALUES +(1); + +-- +-- Dumping data for table `movie_genre` +-- + +INSERT INTO `movie_genre` (`movie_genre_id`, `movie_id`) VALUES +(1, 4), +(1, 8), +(2, 5), +(4, 2), +(5, 1), +(5, 3), +(5, 6), +(6, 1), +(6, 8); + +-- +-- Dumping data for table `movie_genres` +-- + +INSERT INTO `movie_genres` (`id`, `name`) VALUES +(1, 'Kinh dị'), +(2, 'Hài'), +(3, 'Hoạt hình'), +(4, 'Hành động'), +(5, 'Tình cảm'), +(6, 'Gia đình'); + +-- +-- Dumping data for table `movie_genres_seq` +-- + +INSERT INTO `movie_genres_seq` (`next_val`) VALUES +(51); + +-- +-- Dumping data for table `permissions` +-- + +INSERT INTO `permissions` (`id`, `name`, `decription`) VALUES +(1, 'READ_ROLE', NULL), +(2, 'READ_MANAGER', NULL), +(3, 'SAVE_BILL', NULL), +(4, 'READ_BILL', NULL), +(5, 'SAVE_MOVIE', NULL), +(6, 'DELETE_MOVIE', NULL), +(7, 'UPDATE_ROLE', NULL), +(8, 'CREATE_ROLE', NULL), +(9, 'DELETE_ROLE', NULL), +(10, 'READ_PERMISSION', NULL), +(11, 'SAVE_CINEMA', NULL), +(12, 'DELETE_CINEMA', NULL), +(13, 'SAVE_MOVIE_GENRE', NULL), +(14, 'DELETE_MOVIE_GENRE', NULL), +(15, 'DELETE_ROOM', NULL), +(16, 'SAVE_SEAT', NULL), +(17, 'DELETE_SEAT', NULL), +(18, 'SAVE_SEAT_TYPE', NULL), +(19, 'DELETE_SEAT_TYPE', NULL), +(20, 'SAVE_SHOWTIME', NULL), +(21, 'DELETE_SHOWTIME', NULL), +(22, 'READ_USER', NULL), +(23, 'SAVE_USER', NULL), +(24, 'DELETE_USER', NULL); + +-- +-- Dumping data for table `permissions_seq` +-- + +INSERT INTO `permissions_seq` (`next_val`) VALUES +(1); + +-- +-- Dumping data for table `roles` +-- + +INSERT INTO `roles` (`id`, `name`) VALUES +(1, 'ROLE_ADMIN'), +(2, 'ROLE_MANAGER'), +(3, 'GUEST'), +(4, 'testtttttt'), +(5, 'testtt'); + +-- +-- Dumping data for table `roles_seq` +-- + +INSERT INTO `roles_seq` (`next_val`) VALUES +(101); + +-- +-- Dumping data for table `role_permission` +-- + +INSERT INTO `role_permission` (`role_id`, `permission_id`) VALUES +(1, 1), +(1, 2), +(1, 3), +(1, 4), +(1, 5), +(1, 6), +(1, 7), +(1, 8), +(1, 9), +(1, 10), +(1, 11), +(1, 12), +(1, 13), +(1, 14), +(1, 15), +(1, 16), +(1, 17), +(1, 18), +(1, 19), +(1, 20), +(1, 21), +(1, 22), +(1, 23), +(1, 24), +(2, 2); + +-- +-- Dumping data for table `rooms` +-- + +INSERT INTO `rooms` (`id`, `code`, `available_seats`, `name`, `total_seats`, `cinema_id`, `slug`) VALUES +(1, 'Room001', 150, 'RAP 1-Trung Chánh', 150, 1, NULL), +(2, 'Room002', 150, 'RAP 2-Trung Chánh', 150, 1, NULL), +(3, 'Room003', 150, 'RAP 3-Trung Chánh', 150, 1, NULL), +(4, 'Room004', 150, 'RAP 1-Nguyễn Du', 150, 2, NULL), +(5, 'Room005', 150, 'RAP 2-Nguyễn Du', 150, 2, NULL), +(6, 'Room006', 150, 'RAP 1', 150, 3, NULL), +(7, 'Room007', 150, 'RAP 2', 150, 3, NULL); + +-- +-- Dumping data for table `rooms_seq` +-- + +INSERT INTO `rooms_seq` (`next_val`) VALUES +(1); + +-- +-- Dumping data for table `seats` +-- + +INSERT INTO `seats` (`id`, `row_name`, `row_index`, `status`, `room_id`, `seat_type_id`) VALUES +(1, 'A', 1, 1, 1, 1), +(2, 'A', 2, 1, 1, 1), +(3, 'A', 3, 1, 1, 1), +(4, 'A', 4, 1, 1, 1), +(5, 'A', 5, 1, 1, 1), +(6, 'A', 6, 1, 1, 1), +(7, 'A', 7, 1, 1, 1), +(8, 'A', 8, 1, 1, 1), +(9, 'A', 9, 1, 1, 1), +(10, 'A', 10, 1, 1, 1), +(11, 'A', 11, 1, 1, 1), +(12, 'A', 12, 1, 1, 1), +(13, 'A', 13, 1, 1, 1), +(14, 'A', 14, 1, 1, 1), +(15, 'A', 15, 1, 1, 1), +(16, 'B', 1, 1, 1, 1), +(17, 'B', 2, 1, 1, 1), +(18, 'B', 3, 1, 1, 1), +(19, 'B', 4, 1, 1, 1), +(20, 'B', 5, 1, 1, 1), +(21, 'B', 6, 1, 1, 1), +(22, 'B', 7, 1, 1, 1), +(23, 'B', 8, 1, 1, 1), +(24, 'B', 9, 1, 1, 1), +(25, 'B', 10, 1, 1, 1), +(26, 'B', 11, 1, 1, 1), +(27, 'B', 12, 1, 1, 1), +(28, 'B', 13, 1, 1, 1), +(29, 'B', 14, 1, 1, 1), +(30, 'B', 15, 1, 1, 1), +(31, 'C', 1, 1, 1, 1), +(32, 'C', 2, 1, 1, 1), +(33, 'C', 3, 1, 1, 1), +(34, 'C', 4, 1, 1, 1), +(35, 'C', 5, 1, 1, 1), +(36, 'C', 6, 1, 1, 1), +(37, 'C', 7, 1, 1, 1), +(38, 'C', 8, 1, 1, 1), +(39, 'C', 9, 1, 1, 1), +(40, 'C', 10, 1, 1, 1), +(41, 'C', 11, 1, 1, 1), +(42, 'C', 12, 1, 1, 1), +(43, 'C', 13, 1, 1, 1), +(44, 'C', 14, 1, 1, 1), +(45, 'C', 15, 1, 1, 1), +(46, 'D', 1, 1, 1, 1), +(47, 'D', 2, 1, 1, 1), +(48, 'D', 3, 1, 1, 1), +(49, 'D', 4, 1, 1, 1), +(50, 'D', 5, 1, 1, 1), +(51, 'D', 6, 1, 1, 1), +(52, 'D', 7, 1, 1, 1), +(53, 'D', 8, 1, 1, 1), +(54, 'D', 9, 1, 1, 1), +(55, 'D', 10, 1, 1, 1), +(56, 'D', 11, 1, 1, 1), +(57, 'D', 12, 1, 1, 1), +(58, 'D', 13, 1, 1, 1), +(59, 'D', 14, 1, 1, 1), +(60, 'D', 15, 1, 1, 1), +(61, 'E', 1, 1, 1, 1), +(62, 'E', 2, 1, 1, 1), +(63, 'E', 3, 1, 1, 1), +(64, 'E', 4, 1, 1, 1), +(65, 'E', 5, 1, 1, 1), +(66, 'E', 6, 1, 1, 2), +(67, 'E', 7, 1, 1, 2), +(68, 'E', 8, 1, 1, 2), +(69, 'E', 9, 1, 1, 2), +(70, 'E', 10, 1, 1, 2), +(71, 'E', 11, 1, 1, 1), +(72, 'E', 12, 1, 1, 1), +(73, 'E', 13, 1, 1, 1), +(74, 'E', 14, 1, 1, 1), +(75, 'E', 15, 1, 1, 1), +(76, 'F', 1, 1, 1, 1), +(77, 'F', 2, 1, 1, 1), +(78, 'F', 3, 1, 1, 1), +(79, 'F', 4, 1, 1, 1), +(80, 'F', 5, 1, 1, 1), +(81, 'F', 6, 1, 1, 2), +(82, 'F', 7, 1, 1, 2), +(83, 'F', 8, 1, 1, 2), +(84, 'F', 9, 1, 1, 2), +(85, 'F', 10, 1, 1, 2), +(86, 'F', 11, 1, 1, 1), +(87, 'F', 12, 1, 1, 1), +(88, 'F', 13, 1, 1, 1), +(89, 'F', 14, 1, 1, 1), +(90, 'F', 15, 1, 1, 1), +(91, 'G', 1, 1, 1, 1), +(92, 'G', 2, 1, 1, 1), +(93, 'G', 3, 1, 1, 1), +(94, 'G', 4, 1, 1, 1), +(95, 'G', 5, 1, 1, 1), +(96, 'G', 6, 1, 1, 2), +(97, 'G', 7, 1, 1, 2), +(98, 'G', 8, 1, 1, 2), +(99, 'G', 9, 1, 1, 2), +(100, 'G', 10, 1, 1, 2), +(101, 'G', 11, 1, 1, 1), +(102, 'G', 12, 1, 1, 1), +(103, 'G', 13, 1, 1, 1), +(104, 'G', 14, 1, 1, 1), +(105, 'G', 15, 1, 1, 1), +(106, 'H', 1, 1, 1, 1), +(107, 'H', 2, 1, 1, 1), +(108, 'H', 3, 1, 1, 1), +(109, 'H', 4, 1, 1, 1), +(110, 'H', 5, 1, 1, 1), +(111, 'H', 6, 1, 1, 2), +(112, 'H', 7, 1, 1, 2), +(113, 'H', 8, 1, 1, 2), +(114, 'H', 9, 1, 1, 2), +(115, 'H', 10, 1, 1, 2), +(116, 'H', 11, 1, 1, 1), +(117, 'H', 12, 1, 1, 1), +(118, 'H', 13, 1, 1, 1), +(119, 'H', 14, 1, 1, 1), +(120, 'H', 15, 1, 1, 1), +(121, 'I', 1, 1, 1, 1), +(122, 'I', 2, 1, 1, 1), +(123, 'I', 3, 1, 1, 1), +(124, 'I', 4, 1, 1, 1), +(125, 'I', 5, 1, 1, 1), +(126, 'I', 6, 1, 1, 1), +(127, 'I', 7, 1, 1, 1), +(128, 'I', 8, 1, 1, 1), +(129, 'I', 9, 1, 1, 1), +(130, 'I', 10, 1, 1, 1), +(131, 'I', 11, 1, 1, 1), +(132, 'I', 12, 1, 1, 1), +(133, 'I', 13, 1, 1, 1), +(134, 'I', 14, 1, 1, 1), +(135, 'I', 15, 1, 1, 1), +(136, 'J', 1, 1, 1, 1), +(137, 'J', 2, 1, 1, 1), +(138, 'J', 3, 1, 1, 1), +(139, 'J', 4, 1, 1, 1), +(140, 'J', 5, 1, 1, 1), +(141, 'J', 6, 1, 1, 1), +(142, 'J', 7, 1, 1, 1), +(143, 'J', 8, 1, 1, 1), +(144, 'J', 9, 1, 1, 1), +(145, 'J', 10, 1, 1, 1), +(146, 'J', 11, 1, 1, 1), +(147, 'J', 12, 1, 1, 1), +(148, 'J', 13, 1, 1, 1), +(149, 'J', 14, 1, 1, 1), +(150, 'J', 15, 1, 1, 1), +(151, 'A', 1, 1, 2, 1), +(152, 'A', 2, 1, 2, 1), +(153, 'A', 3, 1, 2, 1), +(154, 'A', 4, 1, 2, 1), +(155, 'A', 5, 1, 2, 1), +(156, 'A', 6, 1, 2, 1), +(157, 'A', 7, 1, 2, 1), +(158, 'A', 8, 1, 2, 1), +(159, 'A', 9, 1, 2, 1), +(160, 'A', 10, 1, 2, 1), +(161, 'A', 11, 1, 2, 1), +(162, 'A', 12, 1, 2, 1), +(163, 'A', 13, 1, 2, 1), +(164, 'A', 14, 1, 2, 1), +(165, 'A', 15, 1, 2, 1), +(166, 'B', 1, 1, 2, 1), +(167, 'B', 2, 1, 2, 1), +(168, 'B', 3, 1, 2, 1), +(169, 'B', 4, 1, 2, 1), +(170, 'B', 5, 1, 2, 1), +(171, 'B', 6, 1, 2, 1), +(172, 'B', 7, 1, 2, 1), +(173, 'B', 8, 1, 2, 1), +(174, 'B', 9, 1, 2, 1), +(175, 'B', 10, 1, 2, 1), +(176, 'B', 11, 1, 2, 1), +(177, 'B', 12, 1, 2, 1), +(178, 'B', 13, 1, 2, 1), +(179, 'B', 14, 1, 2, 1), +(180, 'B', 15, 1, 2, 1), +(181, 'C', 1, 1, 2, 1), +(182, 'C', 2, 1, 2, 1), +(183, 'C', 3, 1, 2, 1), +(184, 'C', 4, 1, 2, 1), +(185, 'C', 5, 1, 2, 1), +(186, 'C', 6, 1, 2, 1), +(187, 'C', 7, 1, 2, 1), +(188, 'C', 8, 1, 2, 1), +(189, 'C', 9, 1, 2, 1), +(190, 'C', 10, 1, 2, 1), +(191, 'C', 11, 1, 2, 1), +(192, 'C', 12, 1, 2, 1), +(193, 'C', 13, 1, 2, 1), +(194, 'C', 14, 1, 2, 1), +(195, 'C', 15, 1, 2, 1), +(196, 'D', 1, 1, 2, 1), +(197, 'D', 2, 1, 2, 1), +(198, 'D', 3, 1, 2, 1), +(199, 'D', 4, 1, 2, 1), +(200, 'D', 5, 1, 2, 1), +(201, 'D', 6, 1, 2, 1), +(202, 'D', 7, 1, 2, 1), +(203, 'D', 8, 1, 2, 1), +(204, 'D', 9, 1, 2, 1), +(205, 'D', 10, 1, 2, 1), +(206, 'D', 11, 1, 2, 1), +(207, 'D', 12, 1, 2, 1), +(208, 'D', 13, 1, 2, 1), +(209, 'D', 14, 1, 2, 1), +(210, 'D', 15, 1, 2, 1), +(211, 'E', 1, 1, 2, 1), +(212, 'E', 2, 1, 2, 1), +(213, 'E', 3, 1, 2, 1), +(214, 'E', 4, 1, 2, 1), +(215, 'E', 5, 1, 2, 1), +(216, 'E', 6, 1, 2, 2), +(217, 'E', 7, 1, 2, 2), +(218, 'E', 8, 1, 2, 2), +(219, 'E', 9, 1, 2, 2), +(220, 'E', 10, 1, 2, 2), +(221, 'E', 11, 1, 2, 1), +(222, 'E', 12, 1, 2, 1), +(223, 'E', 13, 1, 2, 1), +(224, 'E', 14, 1, 2, 1), +(225, 'E', 15, 1, 2, 1), +(226, 'F', 1, 1, 2, 1), +(227, 'F', 2, 1, 2, 1), +(228, 'F', 3, 1, 2, 1), +(229, 'F', 4, 1, 2, 1), +(230, 'F', 5, 1, 2, 1), +(231, 'F', 6, 1, 2, 2), +(232, 'F', 7, 1, 2, 2), +(233, 'F', 8, 1, 2, 2), +(234, 'F', 9, 1, 2, 2), +(235, 'F', 10, 1, 2, 2), +(236, 'F', 11, 1, 2, 1), +(237, 'F', 12, 1, 2, 1), +(238, 'F', 13, 1, 2, 1), +(239, 'F', 14, 1, 2, 1), +(240, 'F', 15, 1, 2, 1), +(241, 'G', 1, 1, 2, 1), +(242, 'G', 2, 1, 2, 1), +(243, 'G', 3, 1, 2, 1), +(244, 'G', 4, 1, 2, 1), +(245, 'G', 5, 1, 2, 1), +(246, 'G', 6, 1, 2, 2), +(247, 'G', 7, 1, 2, 2), +(248, 'G', 8, 1, 2, 2), +(249, 'G', 9, 1, 2, 2), +(250, 'G', 10, 1, 2, 2), +(251, 'G', 11, 1, 2, 1), +(252, 'G', 12, 1, 2, 1), +(253, 'G', 13, 1, 2, 1), +(254, 'G', 14, 1, 2, 1), +(255, 'G', 15, 1, 2, 1), +(256, 'H', 1, 1, 2, 1), +(257, 'H', 2, 1, 2, 1), +(258, 'H', 3, 1, 2, 1), +(259, 'H', 4, 1, 2, 1), +(260, 'H', 5, 1, 2, 1), +(261, 'H', 6, 1, 2, 2), +(262, 'H', 7, 1, 2, 2), +(263, 'H', 8, 1, 2, 2), +(264, 'H', 9, 1, 2, 2), +(265, 'H', 10, 1, 2, 2), +(266, 'H', 11, 1, 2, 1), +(267, 'H', 12, 1, 2, 1), +(268, 'H', 13, 1, 2, 1), +(269, 'H', 14, 1, 2, 1), +(270, 'H', 15, 1, 2, 1), +(271, 'I', 1, 1, 2, 1), +(272, 'I', 2, 1, 2, 1), +(273, 'I', 3, 1, 2, 1), +(274, 'I', 4, 1, 2, 1), +(275, 'I', 5, 1, 2, 1), +(276, 'I', 6, 1, 2, 1), +(277, 'I', 7, 1, 2, 1), +(278, 'I', 8, 1, 2, 1), +(279, 'I', 9, 1, 2, 1), +(280, 'I', 10, 1, 2, 1), +(281, 'I', 11, 1, 2, 1), +(282, 'I', 12, 1, 2, 1), +(283, 'I', 13, 1, 2, 1), +(284, 'I', 14, 1, 2, 1), +(285, 'I', 15, 1, 2, 1), +(286, 'J', 1, 1, 2, 1), +(287, 'J', 2, 1, 2, 1), +(288, 'J', 3, 1, 2, 1), +(289, 'J', 4, 1, 2, 1), +(290, 'J', 5, 1, 2, 1), +(291, 'J', 6, 1, 2, 1), +(292, 'J', 7, 1, 2, 1), +(293, 'J', 8, 1, 2, 1), +(294, 'J', 9, 1, 2, 1), +(295, 'J', 10, 1, 2, 1), +(296, 'J', 11, 1, 2, 1), +(297, 'J', 12, 1, 2, 1), +(298, 'J', 13, 1, 2, 1), +(299, 'J', 14, 1, 2, 1), +(300, 'J', 15, 1, 2, 1), +(301, 'A', 1, 1, 3, 1), +(302, 'A', 2, 1, 3, 1), +(303, 'A', 3, 1, 3, 1), +(304, 'A', 4, 1, 3, 1), +(305, 'A', 5, 1, 3, 1), +(306, 'A', 6, 1, 3, 1), +(307, 'A', 7, 1, 3, 1), +(308, 'A', 8, 1, 3, 1), +(309, 'A', 9, 1, 3, 1), +(310, 'A', 10, 1, 3, 1), +(311, 'A', 11, 1, 3, 1), +(312, 'A', 12, 1, 3, 1), +(313, 'A', 13, 1, 3, 1), +(314, 'A', 14, 1, 3, 1), +(315, 'A', 15, 1, 3, 1), +(316, 'B', 1, 1, 3, 1), +(317, 'B', 2, 1, 3, 1), +(318, 'B', 3, 1, 3, 1), +(319, 'B', 4, 1, 3, 1), +(320, 'B', 5, 1, 3, 1), +(321, 'B', 6, 1, 3, 1), +(322, 'B', 7, 1, 3, 1), +(323, 'B', 8, 1, 3, 1), +(324, 'B', 9, 1, 3, 1), +(325, 'B', 10, 1, 3, 1), +(326, 'B', 11, 1, 3, 1), +(327, 'B', 12, 1, 3, 1), +(328, 'B', 13, 1, 3, 1), +(329, 'B', 14, 1, 3, 1), +(330, 'B', 15, 1, 3, 1), +(331, 'C', 1, 1, 3, 1), +(332, 'C', 2, 1, 3, 1), +(333, 'C', 3, 1, 3, 1), +(334, 'C', 4, 1, 3, 1), +(335, 'C', 5, 1, 3, 1), +(336, 'C', 6, 1, 3, 1), +(337, 'C', 7, 1, 3, 1), +(338, 'C', 8, 1, 3, 1), +(339, 'C', 9, 1, 3, 1), +(340, 'C', 10, 1, 3, 1), +(341, 'C', 11, 1, 3, 1), +(342, 'C', 12, 1, 3, 1), +(343, 'C', 13, 1, 3, 1), +(344, 'C', 14, 1, 3, 1), +(345, 'C', 15, 1, 3, 1), +(346, 'D', 1, 1, 3, 1), +(347, 'D', 2, 1, 3, 1), +(348, 'D', 3, 1, 3, 1), +(349, 'D', 4, 1, 3, 1), +(350, 'D', 5, 1, 3, 1), +(351, 'D', 6, 1, 3, 1), +(352, 'D', 7, 1, 3, 1), +(353, 'D', 8, 1, 3, 1), +(354, 'D', 9, 1, 3, 1), +(355, 'D', 10, 1, 3, 1), +(356, 'D', 11, 1, 3, 1), +(357, 'D', 12, 1, 3, 1), +(358, 'D', 13, 1, 3, 1), +(359, 'D', 14, 1, 3, 1), +(360, 'D', 15, 1, 3, 1), +(361, 'E', 1, 1, 3, 1), +(362, 'E', 2, 1, 3, 1), +(363, 'E', 3, 1, 3, 1), +(364, 'E', 4, 1, 3, 1), +(365, 'E', 5, 1, 3, 1), +(366, 'E', 6, 1, 3, 2), +(367, 'E', 7, 1, 3, 2), +(368, 'E', 8, 1, 3, 2), +(369, 'E', 9, 1, 3, 2), +(370, 'E', 10, 1, 3, 2), +(371, 'E', 11, 1, 3, 1), +(372, 'E', 12, 1, 3, 1), +(373, 'E', 13, 1, 3, 1), +(374, 'E', 14, 1, 3, 1), +(375, 'E', 15, 1, 3, 1), +(376, 'F', 1, 1, 3, 1), +(377, 'F', 2, 1, 3, 1), +(378, 'F', 3, 1, 3, 1), +(379, 'F', 4, 1, 3, 1), +(380, 'F', 5, 1, 3, 1), +(381, 'F', 6, 1, 3, 2), +(382, 'F', 7, 1, 3, 2), +(383, 'F', 8, 1, 3, 2), +(384, 'F', 9, 1, 3, 2), +(385, 'F', 10, 1, 3, 2), +(386, 'F', 11, 1, 3, 1), +(387, 'F', 12, 1, 3, 1), +(388, 'F', 13, 1, 3, 1), +(389, 'F', 14, 1, 3, 1), +(390, 'F', 15, 1, 3, 1), +(391, 'G', 1, 1, 3, 1), +(392, 'G', 2, 1, 3, 1), +(393, 'G', 3, 1, 3, 1), +(394, 'G', 4, 1, 3, 1), +(395, 'G', 5, 1, 3, 1), +(396, 'G', 6, 1, 3, 2), +(397, 'G', 7, 1, 3, 2), +(398, 'G', 8, 1, 3, 2), +(399, 'G', 9, 1, 3, 2), +(400, 'G', 10, 1, 3, 2), +(401, 'G', 11, 1, 3, 1), +(402, 'G', 12, 1, 3, 1), +(403, 'G', 13, 1, 3, 1), +(404, 'G', 14, 1, 3, 1), +(405, 'G', 15, 1, 3, 1), +(406, 'H', 1, 1, 3, 1), +(407, 'H', 2, 1, 3, 1), +(408, 'H', 3, 1, 3, 1), +(409, 'H', 4, 1, 3, 1), +(410, 'H', 5, 1, 3, 1), +(411, 'H', 6, 1, 3, 2), +(412, 'H', 7, 1, 3, 2), +(413, 'H', 8, 1, 3, 2), +(414, 'H', 9, 1, 3, 2), +(415, 'H', 10, 1, 3, 2), +(416, 'H', 11, 1, 3, 1), +(417, 'H', 12, 1, 3, 1), +(418, 'H', 13, 1, 3, 1), +(419, 'H', 14, 1, 3, 1), +(420, 'H', 15, 1, 3, 1), +(421, 'I', 1, 1, 3, 1), +(422, 'I', 2, 1, 3, 1), +(423, 'I', 3, 1, 3, 1), +(424, 'I', 4, 1, 3, 1), +(425, 'I', 5, 1, 3, 1), +(426, 'I', 6, 1, 3, 1), +(427, 'I', 7, 1, 3, 1), +(428, 'I', 8, 1, 3, 1), +(429, 'I', 9, 1, 3, 1), +(430, 'I', 10, 1, 3, 1), +(431, 'I', 11, 1, 3, 1), +(432, 'I', 12, 1, 3, 1), +(433, 'I', 13, 1, 3, 1), +(434, 'I', 14, 1, 3, 1), +(435, 'I', 15, 1, 3, 1), +(436, 'J', 1, 1, 3, 1), +(437, 'J', 2, 1, 3, 1), +(438, 'J', 3, 1, 3, 1), +(439, 'J', 4, 1, 3, 1), +(440, 'J', 5, 1, 3, 1), +(441, 'J', 6, 1, 3, 1), +(442, 'J', 7, 1, 3, 1), +(443, 'J', 8, 1, 3, 1), +(444, 'J', 9, 1, 3, 1), +(445, 'J', 10, 1, 3, 1), +(446, 'J', 11, 1, 3, 1), +(447, 'J', 12, 1, 3, 1), +(448, 'J', 13, 1, 3, 1), +(449, 'J', 14, 1, 3, 1), +(450, 'J', 15, 1, 3, 1), +(451, 'A', 1, 1, 4, 1), +(452, 'A', 2, 1, 4, 1), +(453, 'A', 3, 1, 4, 1), +(454, 'A', 4, 1, 4, 1), +(455, 'A', 5, 1, 4, 1), +(456, 'A', 6, 1, 4, 1), +(457, 'A', 7, 1, 4, 1), +(458, 'A', 8, 1, 4, 1), +(459, 'A', 9, 1, 4, 1), +(460, 'A', 10, 1, 4, 1), +(461, 'A', 11, 1, 4, 1), +(462, 'A', 12, 1, 4, 1), +(463, 'A', 13, 1, 4, 1), +(464, 'A', 14, 1, 4, 1), +(465, 'A', 15, 1, 4, 1), +(466, 'B', 1, 1, 4, 1), +(467, 'B', 2, 1, 4, 1), +(468, 'B', 3, 1, 4, 1), +(469, 'B', 4, 1, 4, 1), +(470, 'B', 5, 1, 4, 1), +(471, 'B', 6, 1, 4, 1), +(472, 'B', 7, 1, 4, 1), +(473, 'B', 8, 1, 4, 1), +(474, 'B', 9, 1, 4, 1), +(475, 'B', 10, 1, 4, 1), +(476, 'B', 11, 1, 4, 1), +(477, 'B', 12, 1, 4, 1), +(478, 'B', 13, 1, 4, 1), +(479, 'B', 14, 1, 4, 1), +(480, 'B', 15, 1, 4, 1), +(481, 'C', 1, 1, 4, 1), +(482, 'C', 2, 1, 4, 1), +(483, 'C', 3, 1, 4, 1), +(484, 'C', 4, 1, 4, 1), +(485, 'C', 5, 1, 4, 1), +(486, 'C', 6, 1, 4, 1), +(487, 'C', 7, 1, 4, 1), +(488, 'C', 8, 1, 4, 1), +(489, 'C', 9, 1, 4, 1), +(490, 'C', 10, 1, 4, 1), +(491, 'C', 11, 1, 4, 1), +(492, 'C', 12, 1, 4, 1), +(493, 'C', 13, 1, 4, 1), +(494, 'C', 14, 1, 4, 1), +(495, 'C', 15, 1, 4, 1), +(496, 'D', 1, 1, 4, 1), +(497, 'D', 2, 1, 4, 1), +(498, 'D', 3, 1, 4, 1), +(499, 'D', 4, 1, 4, 1), +(500, 'D', 5, 1, 4, 1), +(501, 'D', 6, 1, 4, 1), +(502, 'D', 7, 1, 4, 1), +(503, 'D', 8, 1, 4, 1), +(504, 'D', 9, 1, 4, 1), +(505, 'D', 10, 1, 4, 1), +(506, 'D', 11, 1, 4, 1), +(507, 'D', 12, 1, 4, 1), +(508, 'D', 13, 1, 4, 1), +(509, 'D', 14, 1, 4, 1), +(510, 'D', 15, 1, 4, 1), +(511, 'E', 1, 1, 4, 1), +(512, 'E', 2, 1, 4, 1), +(513, 'E', 3, 1, 4, 1), +(514, 'E', 4, 1, 4, 1), +(515, 'E', 5, 1, 4, 1), +(516, 'E', 6, 1, 4, 2), +(517, 'E', 7, 1, 4, 2), +(518, 'E', 8, 1, 4, 2), +(519, 'E', 9, 1, 4, 2), +(520, 'E', 10, 1, 4, 2), +(521, 'E', 11, 1, 4, 1), +(522, 'E', 12, 1, 4, 1), +(523, 'E', 13, 1, 4, 1), +(524, 'E', 14, 1, 4, 1), +(525, 'E', 15, 1, 4, 1), +(526, 'F', 1, 1, 4, 1), +(527, 'F', 2, 1, 4, 1), +(528, 'F', 3, 1, 4, 1), +(529, 'F', 4, 1, 4, 1), +(530, 'F', 5, 1, 4, 1), +(531, 'F', 6, 1, 4, 2), +(532, 'F', 7, 1, 4, 2), +(533, 'F', 8, 1, 4, 2), +(534, 'F', 9, 1, 4, 2), +(535, 'F', 10, 1, 4, 2), +(536, 'F', 11, 1, 4, 1), +(537, 'F', 12, 1, 4, 1), +(538, 'F', 13, 1, 4, 1), +(539, 'F', 14, 1, 4, 1), +(540, 'F', 15, 1, 4, 1), +(541, 'G', 1, 1, 4, 1), +(542, 'G', 2, 1, 4, 1), +(543, 'G', 3, 1, 4, 1), +(544, 'G', 4, 1, 4, 1), +(545, 'G', 5, 1, 4, 1), +(546, 'G', 6, 1, 4, 2), +(547, 'G', 7, 1, 4, 2), +(548, 'G', 8, 1, 4, 2), +(549, 'G', 9, 1, 4, 2), +(550, 'G', 10, 1, 4, 2), +(551, 'G', 11, 1, 4, 1), +(552, 'G', 12, 1, 4, 1), +(553, 'G', 13, 1, 4, 1), +(554, 'G', 14, 1, 4, 1), +(555, 'G', 15, 1, 4, 1), +(556, 'H', 1, 1, 4, 1), +(557, 'H', 2, 1, 4, 1), +(558, 'H', 3, 1, 4, 1), +(559, 'H', 4, 1, 4, 1), +(560, 'H', 5, 1, 4, 1), +(561, 'H', 6, 1, 4, 2), +(562, 'H', 7, 1, 4, 2), +(563, 'H', 8, 1, 4, 2), +(564, 'H', 9, 1, 4, 2), +(565, 'H', 10, 1, 4, 2), +(566, 'H', 11, 1, 4, 1), +(567, 'H', 12, 1, 4, 1), +(568, 'H', 13, 1, 4, 1), +(569, 'H', 14, 1, 4, 1), +(570, 'H', 15, 1, 4, 1), +(571, 'I', 1, 1, 4, 1), +(572, 'I', 2, 1, 4, 1), +(573, 'I', 3, 1, 4, 1), +(574, 'I', 4, 1, 4, 1), +(575, 'I', 5, 1, 4, 1), +(576, 'I', 6, 1, 4, 1), +(577, 'I', 7, 1, 4, 1), +(578, 'I', 8, 1, 4, 1), +(579, 'I', 9, 1, 4, 1), +(580, 'I', 10, 1, 4, 1), +(581, 'I', 11, 1, 4, 1), +(582, 'I', 12, 1, 4, 1), +(583, 'I', 13, 1, 4, 1), +(584, 'I', 14, 1, 4, 1), +(585, 'I', 15, 1, 4, 1), +(586, 'J', 1, 1, 4, 1), +(587, 'J', 2, 1, 4, 1), +(588, 'J', 3, 1, 4, 1), +(589, 'J', 4, 1, 4, 1), +(590, 'J', 5, 1, 4, 1), +(591, 'J', 6, 1, 4, 1), +(592, 'J', 7, 1, 4, 1), +(593, 'J', 8, 1, 4, 1), +(594, 'J', 9, 1, 4, 1), +(595, 'J', 10, 1, 4, 1), +(596, 'J', 11, 1, 4, 1), +(597, 'J', 12, 1, 4, 1), +(598, 'J', 13, 1, 4, 1), +(599, 'J', 14, 1, 4, 1), +(600, 'J', 15, 1, 4, 1), +(601, 'A', 1, 1, 5, 1), +(602, 'A', 2, 1, 5, 1), +(603, 'A', 3, 1, 5, 1), +(604, 'A', 4, 1, 5, 1), +(605, 'A', 5, 1, 5, 1), +(606, 'A', 6, 1, 5, 1), +(607, 'A', 7, 1, 5, 1), +(608, 'A', 8, 1, 5, 1), +(609, 'A', 9, 1, 5, 1), +(610, 'A', 10, 1, 5, 1), +(611, 'A', 11, 1, 5, 1), +(612, 'A', 12, 1, 5, 1), +(613, 'A', 13, 1, 5, 1), +(614, 'A', 14, 1, 5, 1), +(615, 'A', 15, 1, 5, 1), +(616, 'B', 1, 1, 5, 1), +(617, 'B', 2, 1, 5, 1), +(618, 'B', 3, 1, 5, 1), +(619, 'B', 4, 1, 5, 1), +(620, 'B', 5, 1, 5, 1), +(621, 'B', 6, 1, 5, 1), +(622, 'B', 7, 1, 5, 1), +(623, 'B', 8, 1, 5, 1), +(624, 'B', 9, 1, 5, 1), +(625, 'B', 10, 1, 5, 1), +(626, 'B', 11, 1, 5, 1), +(627, 'B', 12, 1, 5, 1), +(628, 'B', 13, 1, 5, 1), +(629, 'B', 14, 1, 5, 1), +(630, 'B', 15, 1, 5, 1), +(631, 'C', 1, 1, 5, 1), +(632, 'C', 2, 1, 5, 1), +(633, 'C', 3, 1, 5, 1), +(634, 'C', 4, 1, 5, 1), +(635, 'C', 5, 1, 5, 1), +(636, 'C', 6, 1, 5, 1), +(637, 'C', 7, 1, 5, 1), +(638, 'C', 8, 1, 5, 1), +(639, 'C', 9, 1, 5, 1), +(640, 'C', 10, 1, 5, 1), +(641, 'C', 11, 1, 5, 1), +(642, 'C', 12, 1, 5, 1), +(643, 'C', 13, 1, 5, 1), +(644, 'C', 14, 1, 5, 1), +(645, 'C', 15, 1, 5, 1), +(646, 'D', 1, 1, 5, 1), +(647, 'D', 2, 1, 5, 1), +(648, 'D', 3, 1, 5, 1), +(649, 'D', 4, 1, 5, 1), +(650, 'D', 5, 1, 5, 1), +(651, 'D', 6, 1, 5, 1), +(652, 'D', 7, 1, 5, 1), +(653, 'D', 8, 1, 5, 1), +(654, 'D', 9, 1, 5, 1), +(655, 'D', 10, 1, 5, 1), +(656, 'D', 11, 1, 5, 1), +(657, 'D', 12, 1, 5, 1), +(658, 'D', 13, 1, 5, 1), +(659, 'D', 14, 1, 5, 1), +(660, 'D', 15, 1, 5, 1), +(661, 'E', 1, 1, 5, 1), +(662, 'E', 2, 1, 5, 1), +(663, 'E', 3, 1, 5, 1), +(664, 'E', 4, 1, 5, 1), +(665, 'E', 5, 1, 5, 1), +(666, 'E', 6, 1, 5, 2), +(667, 'E', 7, 1, 5, 2), +(668, 'E', 8, 1, 5, 2), +(669, 'E', 9, 1, 5, 2), +(670, 'E', 10, 1, 5, 2), +(671, 'E', 11, 1, 5, 1), +(672, 'E', 12, 1, 5, 1), +(673, 'E', 13, 1, 5, 1), +(674, 'E', 14, 1, 5, 1), +(675, 'E', 15, 1, 5, 1), +(676, 'F', 1, 1, 5, 1), +(677, 'F', 2, 1, 5, 1), +(678, 'F', 3, 1, 5, 1), +(679, 'F', 4, 1, 5, 1), +(680, 'F', 5, 1, 5, 1), +(681, 'F', 6, 1, 5, 2), +(682, 'F', 7, 1, 5, 2), +(683, 'F', 8, 1, 5, 2), +(684, 'F', 9, 1, 5, 2), +(685, 'F', 10, 1, 5, 2), +(686, 'F', 11, 1, 5, 1), +(687, 'F', 12, 1, 5, 1), +(688, 'F', 13, 1, 5, 1), +(689, 'F', 14, 1, 5, 1), +(690, 'F', 15, 1, 5, 1), +(691, 'G', 1, 1, 5, 1), +(692, 'G', 2, 1, 5, 1), +(693, 'G', 3, 1, 5, 1), +(694, 'G', 4, 1, 5, 1), +(695, 'G', 5, 1, 5, 1), +(696, 'G', 6, 1, 5, 2), +(697, 'G', 7, 1, 5, 2), +(698, 'G', 8, 1, 5, 2), +(699, 'G', 9, 1, 5, 2), +(700, 'G', 10, 1, 5, 2), +(701, 'G', 11, 1, 5, 1), +(702, 'G', 12, 1, 5, 1), +(703, 'G', 13, 1, 5, 1), +(704, 'G', 14, 1, 5, 1), +(705, 'G', 15, 1, 5, 1), +(706, 'H', 1, 1, 5, 1), +(707, 'H', 2, 1, 5, 1), +(708, 'H', 3, 1, 5, 1), +(709, 'H', 4, 1, 5, 1), +(710, 'H', 5, 1, 5, 1), +(711, 'H', 6, 1, 5, 2), +(712, 'H', 7, 1, 5, 2), +(713, 'H', 8, 1, 5, 2), +(714, 'H', 9, 1, 5, 2), +(715, 'H', 10, 1, 5, 2), +(716, 'H', 11, 1, 5, 1), +(717, 'H', 12, 1, 5, 1), +(718, 'H', 13, 1, 5, 1), +(719, 'H', 14, 1, 5, 1), +(720, 'H', 15, 1, 5, 1), +(721, 'I', 1, 1, 5, 1), +(722, 'I', 2, 1, 5, 1), +(723, 'I', 3, 1, 5, 1), +(724, 'I', 4, 1, 5, 1), +(725, 'I', 5, 1, 5, 1), +(726, 'I', 6, 1, 5, 1), +(727, 'I', 7, 1, 5, 1), +(728, 'I', 8, 1, 5, 1), +(729, 'I', 9, 1, 5, 1), +(730, 'I', 10, 1, 5, 1), +(731, 'I', 11, 1, 5, 1), +(732, 'I', 12, 1, 5, 1), +(733, 'I', 13, 1, 5, 1), +(734, 'I', 14, 1, 5, 1), +(735, 'I', 15, 1, 5, 1), +(736, 'J', 1, 1, 5, 1), +(737, 'J', 2, 1, 5, 1), +(738, 'J', 3, 1, 5, 1), +(739, 'J', 4, 1, 5, 1), +(740, 'J', 5, 1, 5, 1), +(741, 'J', 6, 1, 5, 1), +(742, 'J', 7, 1, 5, 1), +(743, 'J', 8, 1, 5, 1), +(744, 'J', 9, 1, 5, 1), +(745, 'J', 10, 1, 5, 1), +(746, 'J', 11, 1, 5, 1), +(747, 'J', 12, 1, 5, 1), +(748, 'J', 13, 1, 5, 1), +(749, 'J', 14, 1, 5, 1), +(750, 'J', 15, 1, 5, 1), +(779, 'A', 1, 1, 6, 1), +(780, 'A', 2, 1, 6, 1), +(781, 'A', 3, 1, 6, 1), +(782, 'A', 4, 1, 6, 1), +(783, 'A', 5, 1, 6, 1), +(784, 'A', 6, 1, 6, 1), +(785, 'A', 7, 1, 6, 1), +(786, 'A', 8, 1, 6, 1), +(787, 'A', 9, 1, 6, 1), +(788, 'A', 10, 1, 6, 1), +(789, 'A', 11, 1, 6, 1), +(790, 'A', 12, 1, 6, 1), +(791, 'A', 13, 1, 6, 1), +(792, 'A', 14, 1, 6, 1), +(793, 'A', 15, 1, 6, 1), +(794, 'B', 1, 1, 6, 1), +(795, 'B', 2, 1, 6, 1), +(796, 'B', 3, 1, 6, 1), +(797, 'B', 4, 1, 6, 1), +(798, 'B', 5, 1, 6, 1), +(799, 'B', 6, 1, 6, 1), +(800, 'B', 7, 1, 6, 1), +(801, 'B', 8, 1, 6, 1), +(802, 'B', 9, 1, 6, 1), +(803, 'B', 10, 1, 6, 1), +(804, 'B', 11, 1, 6, 1), +(805, 'B', 12, 1, 6, 1), +(806, 'B', 13, 1, 6, 1), +(807, 'B', 14, 1, 6, 1), +(808, 'B', 15, 1, 6, 1), +(809, 'C', 1, 1, 6, 1), +(810, 'C', 2, 1, 6, 1), +(811, 'C', 3, 1, 6, 1), +(812, 'C', 4, 1, 6, 1), +(813, 'C', 5, 1, 6, 1), +(814, 'C', 6, 1, 6, 1), +(815, 'C', 7, 1, 6, 1), +(816, 'C', 8, 1, 6, 1), +(817, 'C', 9, 1, 6, 1), +(818, 'C', 10, 1, 6, 1), +(819, 'C', 11, 1, 6, 1), +(820, 'C', 12, 1, 6, 1), +(821, 'C', 13, 1, 6, 1), +(822, 'C', 14, 1, 6, 1), +(823, 'C', 15, 1, 6, 1), +(824, 'D', 1, 1, 6, 1), +(825, 'D', 2, 1, 6, 1), +(826, 'D', 3, 1, 6, 1), +(827, 'D', 4, 1, 6, 1), +(828, 'D', 5, 1, 6, 1), +(829, 'D', 6, 1, 6, 1), +(830, 'D', 7, 1, 6, 1), +(831, 'D', 8, 1, 6, 1), +(832, 'D', 9, 1, 6, 1), +(833, 'D', 10, 1, 6, 1), +(834, 'D', 11, 1, 6, 1), +(835, 'D', 12, 1, 6, 1), +(836, 'D', 13, 1, 6, 1), +(837, 'D', 14, 1, 6, 1), +(838, 'D', 15, 1, 6, 1), +(839, 'E', 1, 1, 6, 1), +(840, 'E', 2, 1, 6, 1), +(841, 'E', 3, 1, 6, 1), +(842, 'E', 4, 1, 6, 1), +(843, 'E', 5, 1, 6, 1), +(844, 'E', 6, 1, 6, 1), +(845, 'E', 7, 1, 6, 1), +(846, 'E', 8, 1, 6, 1), +(847, 'E', 9, 1, 6, 1), +(848, 'E', 10, 1, 6, 1), +(849, 'E', 11, 1, 6, 1), +(850, 'E', 12, 1, 6, 1), +(851, 'E', 13, 1, 6, 1), +(852, 'E', 14, 1, 6, 1), +(853, 'E', 15, 1, 6, 1), +(854, 'F', 1, 1, 6, 1), +(855, 'F', 2, 1, 6, 1), +(856, 'F', 3, 1, 6, 1), +(857, 'F', 4, 1, 6, 1), +(858, 'F', 5, 1, 6, 1), +(859, 'F', 6, 1, 6, 1), +(860, 'F', 7, 1, 6, 1), +(861, 'F', 8, 1, 6, 1), +(862, 'F', 9, 1, 6, 1), +(863, 'F', 10, 1, 6, 1), +(864, 'F', 11, 1, 6, 1), +(865, 'F', 12, 1, 6, 1), +(866, 'F', 13, 1, 6, 1), +(867, 'F', 14, 1, 6, 1), +(868, 'F', 15, 1, 6, 1), +(869, 'G', 1, 1, 6, 1), +(870, 'G', 2, 1, 6, 1), +(871, 'G', 3, 1, 6, 1), +(872, 'G', 4, 1, 6, 1), +(873, 'G', 5, 1, 6, 1), +(874, 'G', 6, 1, 6, 1), +(875, 'G', 7, 1, 6, 1), +(876, 'G', 8, 1, 6, 1), +(877, 'G', 9, 1, 6, 1), +(878, 'G', 10, 1, 6, 1), +(879, 'G', 11, 1, 6, 1), +(880, 'G', 12, 1, 6, 1), +(881, 'G', 13, 1, 6, 1), +(882, 'G', 14, 1, 6, 1), +(883, 'G', 15, 1, 6, 1), +(884, 'H', 1, 1, 6, 1), +(885, 'H', 2, 1, 6, 1), +(886, 'H', 3, 1, 6, 1), +(887, 'H', 4, 1, 6, 1), +(888, 'H', 5, 1, 6, 1), +(889, 'H', 6, 1, 6, 1), +(890, 'H', 7, 1, 6, 1), +(891, 'H', 8, 1, 6, 1), +(892, 'H', 9, 1, 6, 1), +(893, 'H', 10, 1, 6, 1), +(894, 'H', 11, 1, 6, 1), +(895, 'H', 12, 1, 6, 1), +(896, 'H', 13, 1, 6, 1), +(897, 'H', 14, 1, 6, 1), +(898, 'H', 15, 1, 6, 1), +(899, 'I', 1, 1, 6, 1), +(900, 'I', 2, 1, 6, 1), +(901, 'I', 3, 1, 6, 1), +(902, 'I', 4, 1, 6, 1), +(903, 'I', 5, 1, 6, 1), +(904, 'I', 6, 1, 6, 1), +(905, 'I', 7, 1, 6, 1), +(906, 'I', 8, 1, 6, 1), +(907, 'I', 9, 1, 6, 1), +(908, 'I', 10, 1, 6, 1), +(909, 'I', 11, 1, 6, 1), +(910, 'I', 12, 1, 6, 1), +(911, 'I', 13, 1, 6, 1), +(912, 'I', 14, 1, 6, 1), +(913, 'I', 15, 1, 6, 1), +(914, 'J', 1, 1, 6, 1), +(915, 'J', 2, 1, 6, 1), +(916, 'J', 3, 1, 6, 1), +(917, 'J', 4, 1, 6, 1), +(918, 'J', 5, 1, 6, 1), +(919, 'J', 6, 1, 6, 1), +(920, 'J', 7, 1, 6, 1), +(921, 'J', 8, 1, 6, 1), +(922, 'J', 9, 1, 6, 1), +(923, 'J', 10, 1, 6, 1), +(924, 'J', 11, 1, 6, 1), +(925, 'J', 12, 1, 6, 1), +(926, 'J', 13, 1, 6, 1), +(927, 'J', 14, 1, 6, 1), +(928, 'J', 15, 1, 6, 1), +(1079, 'A', 1, 1, 7, 1), +(1080, 'A', 2, 1, 7, 1), +(1081, 'A', 3, 1, 7, 1), +(1082, 'A', 4, 1, 7, 1), +(1083, 'A', 5, 1, 7, 1), +(1084, 'A', 6, 1, 7, 1), +(1085, 'A', 7, 1, 7, 1), +(1086, 'A', 8, 1, 7, 1), +(1087, 'A', 9, 1, 7, 1), +(1088, 'A', 10, 1, 7, 1), +(1089, 'A', 11, 1, 7, 1), +(1090, 'A', 12, 1, 7, 1), +(1091, 'A', 13, 1, 7, 1), +(1092, 'A', 14, 1, 7, 1), +(1093, 'A', 15, 1, 7, 1), +(1094, 'B', 1, 1, 7, 1), +(1095, 'B', 2, 1, 7, 1), +(1096, 'B', 3, 1, 7, 1), +(1097, 'B', 4, 1, 7, 1), +(1098, 'B', 5, 1, 7, 1), +(1099, 'B', 6, 1, 7, 1), +(1100, 'B', 7, 1, 7, 1), +(1101, 'B', 8, 1, 7, 1), +(1102, 'B', 9, 1, 7, 1), +(1103, 'B', 10, 1, 7, 1), +(1104, 'B', 11, 1, 7, 1), +(1105, 'B', 12, 1, 7, 1), +(1106, 'B', 13, 1, 7, 1), +(1107, 'B', 14, 1, 7, 1), +(1108, 'B', 15, 1, 7, 1), +(1109, 'C', 1, 1, 7, 1), +(1110, 'C', 2, 1, 7, 1), +(1111, 'C', 3, 1, 7, 1), +(1112, 'C', 4, 1, 7, 1), +(1113, 'C', 5, 1, 7, 1), +(1114, 'C', 6, 1, 7, 1), +(1115, 'C', 7, 1, 7, 1), +(1116, 'C', 8, 1, 7, 1), +(1117, 'C', 9, 1, 7, 1), +(1118, 'C', 10, 1, 7, 1), +(1119, 'C', 11, 1, 7, 1), +(1120, 'C', 12, 1, 7, 1), +(1121, 'C', 13, 1, 7, 1), +(1122, 'C', 14, 1, 7, 1), +(1123, 'C', 15, 1, 7, 1), +(1124, 'D', 1, 1, 7, 1), +(1125, 'D', 2, 1, 7, 1), +(1126, 'D', 3, 1, 7, 1), +(1127, 'D', 4, 1, 7, 1), +(1128, 'D', 5, 1, 7, 1), +(1129, 'D', 6, 1, 7, 1), +(1130, 'D', 7, 1, 7, 1), +(1131, 'D', 8, 1, 7, 1), +(1132, 'D', 9, 1, 7, 1), +(1133, 'D', 10, 1, 7, 1), +(1134, 'D', 11, 1, 7, 1), +(1135, 'D', 12, 1, 7, 1), +(1136, 'D', 13, 1, 7, 1), +(1137, 'D', 14, 1, 7, 1), +(1138, 'D', 15, 1, 7, 1), +(1139, 'E', 1, 1, 7, 1), +(1140, 'E', 2, 1, 7, 1), +(1141, 'E', 3, 1, 7, 1), +(1142, 'E', 4, 1, 7, 1), +(1143, 'E', 5, 1, 7, 1), +(1144, 'E', 6, 1, 7, 1), +(1145, 'E', 7, 1, 7, 1), +(1146, 'E', 8, 1, 7, 1), +(1147, 'E', 9, 1, 7, 1), +(1148, 'E', 10, 1, 7, 1), +(1149, 'E', 11, 1, 7, 1), +(1150, 'E', 12, 1, 7, 1), +(1151, 'E', 13, 1, 7, 1), +(1152, 'E', 14, 1, 7, 1), +(1153, 'E', 15, 1, 7, 1), +(1154, 'F', 1, 1, 7, 1), +(1155, 'F', 2, 1, 7, 1), +(1156, 'F', 3, 1, 7, 1), +(1157, 'F', 4, 1, 7, 1), +(1158, 'F', 5, 1, 7, 1), +(1159, 'F', 6, 1, 7, 1), +(1160, 'F', 7, 1, 7, 1), +(1161, 'F', 8, 1, 7, 1), +(1162, 'F', 9, 1, 7, 1), +(1163, 'F', 10, 1, 7, 1), +(1164, 'F', 11, 1, 7, 1), +(1165, 'F', 12, 1, 7, 1), +(1166, 'F', 13, 1, 7, 1), +(1167, 'F', 14, 1, 7, 1), +(1168, 'F', 15, 1, 7, 1), +(1169, 'G', 1, 1, 7, 1), +(1170, 'G', 2, 1, 7, 1), +(1171, 'G', 3, 1, 7, 1), +(1172, 'G', 4, 1, 7, 1), +(1173, 'G', 5, 1, 7, 1), +(1174, 'G', 6, 1, 7, 1), +(1175, 'G', 7, 1, 7, 1), +(1176, 'G', 8, 1, 7, 1), +(1177, 'G', 9, 1, 7, 1), +(1178, 'G', 10, 1, 7, 1), +(1179, 'G', 11, 1, 7, 1), +(1180, 'G', 12, 1, 7, 1), +(1181, 'G', 13, 1, 7, 1), +(1182, 'G', 14, 1, 7, 1), +(1183, 'G', 15, 1, 7, 1), +(1184, 'H', 1, 1, 7, 1), +(1185, 'H', 2, 1, 7, 1), +(1186, 'H', 3, 1, 7, 1), +(1187, 'H', 4, 1, 7, 1), +(1188, 'H', 5, 1, 7, 1), +(1189, 'H', 6, 1, 7, 1), +(1190, 'H', 7, 1, 7, 1), +(1191, 'H', 8, 1, 7, 1), +(1192, 'H', 9, 1, 7, 1), +(1193, 'H', 10, 1, 7, 1), +(1194, 'H', 11, 1, 7, 1), +(1195, 'H', 12, 1, 7, 1), +(1196, 'H', 13, 1, 7, 1), +(1197, 'H', 14, 1, 7, 1), +(1198, 'H', 15, 1, 7, 1), +(1199, 'I', 1, 1, 7, 1), +(1200, 'I', 2, 1, 7, 1), +(1201, 'I', 3, 1, 7, 1), +(1202, 'I', 4, 1, 7, 1), +(1203, 'I', 5, 1, 7, 1), +(1204, 'I', 6, 1, 7, 1), +(1205, 'I', 7, 1, 7, 1), +(1206, 'I', 8, 1, 7, 1), +(1207, 'I', 9, 1, 7, 1), +(1208, 'I', 10, 1, 7, 1), +(1209, 'I', 11, 1, 7, 1), +(1210, 'I', 12, 1, 7, 1), +(1211, 'I', 13, 1, 7, 1), +(1212, 'I', 14, 1, 7, 1), +(1213, 'I', 15, 1, 7, 1), +(1214, 'J', 1, 1, 7, 1), +(1215, 'J', 2, 1, 7, 1), +(1216, 'J', 3, 1, 7, 1), +(1217, 'J', 4, 1, 7, 1), +(1218, 'J', 5, 1, 7, 1), +(1219, 'J', 6, 1, 7, 1), +(1220, 'J', 7, 1, 7, 1), +(1221, 'J', 8, 1, 7, 1), +(1222, 'J', 9, 1, 7, 1), +(1223, 'J', 10, 1, 7, 1), +(1224, 'J', 11, 1, 7, 1), +(1225, 'J', 12, 1, 7, 1), +(1226, 'J', 13, 1, 7, 1), +(1227, 'J', 14, 1, 7, 1), +(1228, 'J', 15, 1, 7, 1); + +-- +-- Dumping data for table `seats_seq` +-- + +INSERT INTO `seats_seq` (`next_val`) VALUES +(1); + +-- +-- Dumping data for table `seat_types` +-- + +INSERT INTO `seat_types` (`id`, `name`, `price`) VALUES +(1, 'Normal', 50000), +(2, 'VIP', 70000), +(3, 'string', 0); + +-- +-- Dumping data for table `seat_types_seq` +-- + +INSERT INTO `seat_types_seq` (`next_val`) VALUES +(1); + +-- +-- Dumping data for table `showtimes` +-- + +INSERT INTO `showtimes` (`id`, `running_time`, `start_date`, `start_time`, `status`, `room_id`, `movie_id`, `code`) VALUES +(113, 149, '2023-12-05', '13:30:00.000000', 1, 2, 3, '00149756-dcee-4202-9553-6da76a19fce0'), +(114, 123, '2023-12-05', '17:30:00.000000', 1, 5, 4, '03f8cc4e-b4fa-475f-a9c6-67e4240fd450'), +(115, 131, '2023-11-28', '16:30:00.000000', 0, 1, 2, '054a0105-e1a3-46c3-9e03-bc3983fce9a9'), +(116, 123, '2023-12-05', '15:15:00.000000', 1, 5, 4, '0622bfbc-e5ab-4493-b967-4254621ff2df'), +(117, 131, '2023-11-28', '14:45:00.000000', 0, 6, 2, '07706194-c525-451d-9efb-ec200a88df2d'), +(118, 133, '2023-12-12', '10:00:00.000000', 1, 1, 6, '0846bb01-ee85-4c90-86c3-482945280a6e'), +(119, 150, '2023-12-05', '12:45:00.000000', 1, 3, 7, '0ad362d5-86d5-4a10-b469-9d9fbd21b7ad'), +(120, 131, '2023-12-04', '17:45:00.000000', 0, 5, 2, '0e385cdb-aa71-4463-be2b-29edc1263b9b'), +(121, 120, '2023-10-28', '13:30:00.000000', 0, 2, 1, '1'), +(122, 120, '2023-10-29', '13:00:00.000000', 0, 1, 1, '123'), +(123, 133, '2023-12-05', '09:00:00.000000', 0, 2, 6, '1445a9b1-7477-4ae9-ab79-a768e0b7894f'), +(124, 131, '2023-12-04', '13:00:00.000000', 0, 2, 2, '14e902b0-a9ee-4537-b379-66bf6c56d5c9'), +(125, 131, '2023-12-04', '08:30:00.000000', 0, 2, 2, '19d1b88d-3f7f-4b37-bf22-5ce346424aaf'), +(126, 120, '2023-10-28', '13:00:00.000000', 0, 4, 1, '2'), +(127, 149, '2023-12-08', '22:00:00.000000', 1, 6, 3, '269c2d83-18d1-418c-8662-df0d997cbba4'), +(128, 133, '2023-12-01', '08:00:00.000000', 0, 1, 6, '28caabee-abb5-42d7-9772-b6d52be89543'), +(129, 133, '2023-11-29', '09:00:00.000000', 0, 3, 6, '2b2c2684-0726-425a-b675-8c7638dfa679'), +(130, 120, '2023-10-28', '16:00:00.000000', 0, 4, 1, '3'), +(131, 131, '2023-12-04', '17:30:00.000000', 0, 2, 2, '342f0a2d-8f8e-46f6-846a-440cfe9c4825'), +(132, 149, '2023-12-07', '09:00:00.000000', 1, 3, 3, '367cb014-ccc3-4329-94e0-641b683b1ffa'), +(133, 148, '2023-11-30', '09:00:00.000000', 0, 2, 5, '3a11aa4b-b324-47a2-9dea-5a1a9defcae7'), +(134, 120, '2023-10-28', '16:00:00.000000', 0, 1, 2, '4'), +(135, 131, '2023-12-05', '00:30:00.000000', 0, 5, 2, '423c70bb-72f8-4549-8cb1-0a3dfe3c0caa'), +(136, 149, '2023-12-09', '10:15:00.000000', 1, 3, 3, '47b7ce72-13da-4050-b560-5fbd21ae9073'), +(137, 131, '2023-12-04', '10:45:00.000000', 0, 2, 2, '4a25f30e-394b-42ce-b5f1-9b48a765e34c'), +(138, 148, '2023-12-01', '17:45:00.000000', 0, 1, 5, '5e89e4f9-5148-4de8-bb22-416b3dcff264'), +(139, 133, '2023-12-05', '22:15:00.000000', 1, 5, 6, '6160107f-386c-4c7e-8b05-96ace678481c'), +(140, 131, '2023-12-05', '10:30:00.000000', 0, 5, 2, '62340799-4965-446f-be95-51bc11422348'), +(141, 148, '2023-12-06', '09:00:00.000000', 1, 1, 5, '62ea7db9-f048-4148-8ee9-e44ab01c8300'), +(142, 131, '2023-12-05', '16:30:00.000000', 1, 6, 2, '63a34907-fda5-44ef-83ad-cc80c7f40356'), +(143, 131, '2023-12-05', '08:15:00.000000', 0, 5, 2, '640be26d-e2ee-412f-a7f9-5d360e55b303'), +(144, 133, '2023-11-29', '16:30:00.000000', 0, 1, 6, '68c438c1-13c8-4fd7-94ee-58634b4747ba'), +(145, 149, '2023-12-07', '09:00:00.000000', 1, 1, 3, '6fcf601d-ff79-4a92-98f0-3b90c242010f'), +(146, 150, '2023-12-05', '12:45:00.000000', 1, 5, 7, '74faae1a-175a-4663-855e-0ec1738f57c2'), +(147, 123, '2023-12-09', '18:15:00.000000', 1, 6, 4, '75bc4720-e22f-4fa7-bbf2-e8f04665a851'), +(148, 123, '2023-12-05', '02:00:00.000000', 0, 6, 4, '77c52cd4-194d-4aed-940d-a96dab7a34ff'), +(149, 131, '2023-11-28', '14:00:00.000000', 0, 5, 2, '7890ab10-aee5-4775-aef4-66b1e6ccaf46'), +(150, 133, '2023-12-06', '11:30:00.000000', 1, 3, 6, '7f54ac19-b166-4b30-b894-4a8d08a2f192'), +(151, 149, '2023-11-30', '14:00:00.000000', 0, 3, 3, '85ab5ae5-9de8-497a-9cbf-17de58b84fe2'), +(152, 148, '2023-12-01', '12:45:00.000000', 0, 1, 5, '86851ee1-dca6-4d3a-ba52-061626460872'), +(153, 131, '2023-12-11', '07:45:00.000000', 1, 1, 2, '86e7d5ba-89e1-4e0b-9ef7-600cf506f805'), +(154, 131, '2023-12-05', '15:45:00.000000', 1, 1, 2, '88fad81a-28ff-4ae9-8565-80415bb6ee9a'), +(155, 131, '2023-12-06', '07:00:00.000000', 1, 4, 2, '8beb89fa-3f45-4458-a21e-cea642e931e9'), +(156, 148, '2023-11-30', '16:30:00.000000', 0, 3, 5, '8c88179c-8495-4641-bbd9-7425db125da9'), +(157, 148, '2023-11-29', '14:00:00.000000', 0, 2, 5, '8e9874a2-e071-4fcc-9f6f-851a8ae4adc0'), +(158, 131, '2023-12-08', '16:30:00.000000', 1, 6, 2, '8f6285b5-20d6-40ae-83ff-985931aea2e2'), +(159, 131, '2023-12-05', '19:45:00.000000', 1, 2, 2, '920c4c8c-2411-4eee-a294-72656c28eda7'), +(160, 131, '2023-12-11', '10:00:00.000000', 1, 1, 2, '92a27614-50fd-4657-a2cc-708c8f1591a5'), +(161, 131, '2023-12-11', '12:15:00.000000', 1, 1, 2, '96065179-2b58-4520-be83-2111134f1fac'), +(162, 131, '2023-12-04', '08:15:00.000000', 0, 5, 2, '96b5093a-3ce7-4dac-9a9d-6bce443b64fd'), +(163, 148, '2023-12-01', '15:15:00.000000', 0, 1, 5, '972b3180-d66b-4185-b41f-52cd81bb5517'), +(164, 150, '2023-12-04', '13:00:00.000000', 0, 1, 7, '9a2d9107-8710-49f9-ba22-38276fe54497'), +(165, 133, '2023-12-05', '09:00:00.000000', 0, 1, 6, '9a6b57e4-4cc9-459b-840c-436f4576d920'), +(166, 149, '2023-12-08', '09:15:00.000000', 1, 3, 3, '9a9d10bf-fb02-4ca5-a453-058e88954311'), +(167, 149, '2023-12-04', '12:45:00.000000', 0, 5, 3, '9d1dea97-d9b9-4dda-bcd8-29e526806493'), +(168, 131, '2023-12-04', '19:45:00.000000', 0, 2, 2, '9e8e0faa-0226-49ac-9422-d28a9a39eb61'), +(169, 133, '2023-12-02', '15:15:00.000000', 0, 1, 6, '9ecac6a8-4b72-4951-8e5a-a748c883e34f'), +(170, 133, '2023-11-28', '18:45:00.000000', 0, 1, 6, 'a2c6bf8d-2167-49c3-9bf7-dbc1918a5754'), +(171, 131, '2023-11-28', '11:45:00.000000', 0, 4, 2, 'b1468b44-e512-4275-81c8-f2b2398c64db'), +(172, 133, '2023-12-05', '15:15:00.000000', 1, 3, 6, 'b2cd0602-6d68-4cb1-856d-5eb548f02a9b'), +(173, 149, '2023-12-04', '15:15:00.000000', 0, 5, 3, 'b4dd0a43-ccdd-4e70-9262-8e673b4ff0e9'), +(174, 133, '2023-12-06', '23:15:00.000000', 1, 4, 6, 'b7872071-2d9b-4a69-8d35-adcdbe0c45de'), +(175, 131, '2023-12-04', '15:15:00.000000', 0, 2, 2, 'c0aa21ff-78ee-479b-8f15-a75e2729d76f'), +(176, 133, '2023-12-05', '13:30:00.000000', 1, 1, 6, 'c406a864-b8d8-4c48-b8ba-c7a8919c34fe'), +(177, 149, '2023-12-04', '15:30:00.000000', 0, 1, 3, 'c447fc6e-b5e8-4e3d-a826-9ab400a0b801'), +(178, 148, '2023-12-01', '10:15:00.000000', 0, 1, 5, 'c6a6b102-f18d-4d1b-b17e-341170b13264'), +(179, 150, '2023-12-13', '12:30:00.000000', 1, 1, 7, 'daf5a711-53ff-47d9-ad27-6ff229006c6b'), +(180, 131, '2023-11-29', '12:00:00.000000', 0, 4, 2, 'db621837-700e-4a1f-98b6-b855b4b2df72'), +(181, 148, '2023-12-05', '19:45:00.000000', 1, 5, 5, 'dcc79ae2-5260-457a-b577-3204a4cf3e3b'), +(182, 133, '2023-12-04', '10:30:00.000000', 0, 5, 6, 'de584427-847a-46f7-8263-bc4fef9c994a'), +(183, 148, '2023-12-06', '18:30:00.000000', 1, 6, 5, 'e02b4957-8860-4048-a0ba-a03627313062'), +(184, 133, '2023-12-05', '11:30:00.000000', 0, 6, 6, 'e0d3cbfb-481e-4dd1-9b2b-b3bb3653add6'), +(185, 133, '2023-12-05', '17:30:00.000000', 1, 2, 6, 'ea674ec8-6094-4e35-8255-e557872bae3e'), +(186, 131, '2023-12-04', '22:15:00.000000', 0, 5, 2, 'ec786d13-dd84-4485-a773-942da7ac0764'), +(187, 123, '2023-12-04', '20:00:00.000000', 0, 5, 4, 'f0bf82c0-326e-4488-a504-cefab3be0512'), +(188, 131, '2023-12-05', '11:15:00.000000', 0, 2, 2, 'f2cab68e-d247-4807-8675-c50309ba9062'), +(189, 131, '2023-12-05', '07:00:00.000000', 0, 4, 2, 'f32c67d6-abf4-43d9-8cfd-0ae40495c5ba'), +(190, 131, '2023-12-05', '09:00:00.000000', 0, 3, 2, 'f59c2920-9f82-49ec-8673-f8e5d7c2ee0f'), +(191, 131, '2023-12-04', '10:45:00.000000', 0, 1, 2, 'f74ddbb4-c34d-4684-97ea-99f7af0f4ef2'), +(192, 133, '2023-12-06', '09:00:00.000000', 1, 3, 6, 'fcb9eeb5-01d7-4521-aa2e-00f4de2534c7'), +(193, 149, '2023-12-06', '08:00:00.000000', 1, 6, 3, 'fd77ae0a-5365-47bd-9d19-9e457c675816'), +(194, 123, '2023-12-05', '11:15:00.000000', 0, 1, 4, 'fe9e5256-eeb1-4af3-9625-6226dce91bef'), +(195, 120, '2023-11-02', '09:00:00.000000', 0, 1, 1, 'Showtime006'), +(196, 120, '2023-11-02', '12:00:00.000000', 0, 1, 1, 'Showtime007'), +(197, 120, '2023-11-02', '15:00:00.000000', 0, 1, 1, 'Showtime008'), +(198, 120, '2023-11-02', '18:00:00.000000', 0, 1, 1, 'Showtime009'), +(199, 120, '2023-11-02', '09:30:00.000000', 0, 2, 1, 'Showtime010'), +(200, 120, '2023-11-03', '21:30:00.000000', 0, 4, 1, 'Showtime011'), +(201, 120, '2023-11-02', '23:30:00.000000', 0, 2, 1, 'Showtime012'), +(202, 120, '2023-11-02', '22:00:00.000000', 0, 2, 1, 'Showtime013'), +(203, 120, '2023-11-01', '09:00:00.000000', 0, 1, 4, 'Showtime014'), +(204, 120, '2023-11-01', '12:00:00.000000', 0, 1, 4, 'Showtime015'), +(205, 120, '2023-11-01', '15:00:00.000000', 0, 1, 4, 'Showtime016'), +(206, 120, '2023-11-01', '18:00:00.000000', 0, 1, 4, 'Showtime017'), +(207, 120, '2023-11-01', '09:30:00.000000', 0, 2, 4, 'Showtime018'), +(208, 120, '2023-11-01', '12:30:00.000000', 0, 2, 4, 'Showtime019'), +(209, 120, '2023-11-04', '09:30:00.000000', 0, 4, 4, 'Showtime020'), +(210, 120, '2023-11-01', '10:30:00.000000', 0, 5, 4, 'Showtime021'), +(211, 120, '2023-11-07', '16:30:00.000000', 0, 4, 1, 'Showtime022'), +(212, 120, '2023-11-02', '23:00:00.000000', 0, 5, 1, 'Showtime023'), +(213, 120, '2023-11-02', '09:30:00.000000', 0, 4, 4, 'Showtime024'), +(214, 120, '2023-11-02', '10:30:00.000000', 0, 5, 4, 'Showtime025'), +(215, 120, '2023-11-01', '13:30:00.000000', 0, 4, 1, 'Showtime026'), +(216, 120, '2023-11-01', '17:00:00.000000', 0, 5, 1, 'Showtime027'), +(217, 136, '2023-11-15', '09:00:00.000000', 0, 1, 1, 'Showtime028'), +(218, 136, '2023-11-15', '11:00:00.000000', 0, 2, 1, 'Showtime029'), +(219, 136, '2023-11-16', '11:30:00.000000', 0, 1, 1, 'Showtime030'), +(220, 136, '2023-11-15', '13:30:00.000000', 0, 2, 1, 'Showtime031'), +(221, 136, '2023-11-25', '14:00:00.000000', 0, 1, 1, 'Showtime032'), +(222, 136, '2023-11-25', '16:30:00.000000', 0, 1, 1, 'Showtime033'), +(223, 123, '2023-11-25', '19:00:00.000000', 0, 1, 4, 'Showtime034'), +(224, 136, '2023-11-25', '21:15:00.000000', 0, 1, 1, 'Showtime035'); + +-- +-- Dumping data for table `showtimes_seq` +-- + +INSERT INTO `showtimes_seq` (`next_val`) VALUES +(1); + +-- +-- Dumping data for table `tickets` +-- + +INSERT INTO `tickets` (`id`, `bill_id`, `showtime_id`, `seat_id`) VALUES +(0x0d1a0762126e361f87a60d9fe0937084, 0xb76524a502bd4c51983286e933863f57, 187, 698), +(0x0eaa267501da3e8b81cdda80180f7b3c, 0x442b8ee77f6d48ae866617a41b9ac882, 133, 170), +(0x0feedaf04073388db39e6dbfe945058c, 0x516cdf92447d46d2b2f713c67985233f, 217, 4), +(0x103331950f8e3f409975d2c089b21a51, 0x442b8ee77f6d48ae866617a41b9ac882, 133, 171), +(0x1bd3a95b48dc310fa1210017f5743c52, 0x78598fef81524cbd9bf821620ce42a45, 202, 167), +(0x1bf4708ee8a5372d8a88b9ec7b8774f3, 0xc86a74f8d7f24afb9675bc306c64e365, 157, 221), +(0x22efebe35c5e3c0393eaa0ec5096342c, 0xd464eb75b912498e809905a9f48ce78b, 223, 148), +(0x24a7f631126f3e15aa40f0f6e00808ff, 0xc86a74f8d7f24afb9675bc306c64e365, 157, 222), +(0x2991b86feaf843ba92f3e636ecf6428a, 0x53db1ce614924807980804a3dc7765c5, 118, 5), +(0x2e5889ab9cd2320cb9d6c866cbf5ad26, 0xe772c5cc982d45a2a3d80e0a7e735fdf, 223, 76), +(0x33e93705b23932c2a3470f1040479da9, 0xe772c5cc982d45a2a3d80e0a7e735fdf, 223, 55), +(0x37cab3ead28731ae82a3174ef8d59d46, 0x577cc36a800e42fb956fd191b5cd6d94, 122, 81), +(0x3eea96cb030b3a99b413c6f956e64704, 0x79e2ac2bd0c14ae0878f5e558d782d3b, 134, 111), +(0x59dda3f9eb7e3abdacb595ad9693da95, 0xc86a74f8d7f24afb9675bc306c64e365, 157, 223), +(0x5a2e76412a6d317e8034377e861f3a62, 0xe772c5cc982d45a2a3d80e0a7e735fdf, 223, 93), +(0x5a7017e75f113f819956621f09619d36, 0x516cdf92447d46d2b2f713c67985233f, 217, 2), +(0x5dfc94554c583a488083daf52da2c314, 0x516cdf92447d46d2b2f713c67985233f, 217, 3), +(0x5e66b73b625937f88df88973d0fe0587, 0x577cc36a800e42fb956fd191b5cd6d94, 122, 82), +(0x76a7e4a26ef63b31b5bd88fcbd927858, 0xb76524a502bd4c51983286e933863f57, 187, 696), +(0x93c1fe57114636d887e8192d98d7a16d, 0xb76524a502bd4c51983286e933863f57, 187, 697), +(0xaa479837989c3799989674d118d75585, 0xd464eb75b912498e809905a9f48ce78b, 223, 149), +(0xafa4aab5a80234e7933c9851f309a0b4, 0x79e2ac2bd0c14ae0878f5e558d782d3b, 134, 113), +(0xb315444b81aa3b77a058736976d88730, 0xd464eb75b912498e809905a9f48ce78b, 223, 145), +(0xb50db180eb1b39c0970bea4de85c8879, 0x79e2ac2bd0c14ae0878f5e558d782d3b, 134, 112), +(0xcc3a92b029a044bcb329fbe2851b79ce, 0x53db1ce614924807980804a3dc7765c5, 118, 3), +(0xdcad3df878923ebe983c8c35d8736825, 0xd464eb75b912498e809905a9f48ce78b, 223, 147), +(0xddcaeb02c95a34f68413334694dd4ced, 0x516cdf92447d46d2b2f713c67985233f, 217, 1), +(0xdebbef4c24153048ab507f9c6a2293a4, 0x6ad7e232c24249a8b658d15dcbf2f353, 133, 155), +(0xe7cc68337f1f3912ab9695fc6a4d278f, 0xc86a74f8d7f24afb9675bc306c64e365, 157, 225), +(0xe93933fc87cd3dd3b8762a1ca83fcb56, 0x6ad7e232c24249a8b658d15dcbf2f353, 133, 156), +(0xed0d52cc16903652b32c37c593d0cb22, 0x577cc36a800e42fb956fd191b5cd6d94, 122, 80), +(0xf37b806c8d8c3a88bd0ef372a187c284, 0xc86a74f8d7f24afb9675bc306c64e365, 157, 224), +(0xfb0f1568b28133f8b49e41b437b085ab, 0xd464eb75b912498e809905a9f48ce78b, 223, 146), +(0xfecb715536303ab1a1481edcf8067328, 0xe772c5cc982d45a2a3d80e0a7e735fdf, 157, 200); + +-- +-- Dumping data for table `users` +-- + +INSERT INTO `users` (`id`, `email`, `password`, `date_of_birth`, `role_id`, `verify`, `create_date`, `full_name`, `verify_account`, `verify_mail`, `verify_pass`, `gender`, `phone_number`, `avatar`, `provider_id`, `provider`) VALUES +(0x15f2378dd8a63cb3a0123470dd6758aa, 'hn2666@gmail.com', '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', NULL, 3, b'0', '2023-12-01 00:37:15.081016', '2sds@', NULL, NULL, NULL, 'UNKNOWN', NULL, 'https://cdn.discordapp.com/attachments/1168144426141499412/1169264779769155594/c6e56503cfdd87da299f72dc416023d4.jpg?ex=6554c5af&is=654250af&hm=440ebc745e441103a1824a80edb22f2d1c371743e8d7a4d413dbbc62eae31554&', NULL, 'LOCAL'), +(0x1df3a4a44c2a425593437c279e7f3d02, 'contact.duynguyen@gmail.com', '123', NULL, NULL, NULL, '2024-06-14 15:39:31.427905', 'Duy Nguyễn Thanh', NULL, NULL, NULL, 'UNKNOWN', NULL, 'https://lh3.googleusercontent.com/a/ACg8ocIM44Vn_Q_RWwG1G1THLqPHwOPk_MPva4b2ghwx8paKSTh0Dg=s96-c', 'google', 'GOOGLE'), +(0x49c64e33d27338efa68c23e1aa16e499, 'nngocsang38@gmail.com', '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', '2003-08-21', 1, b'1', '2023-10-16 14:20:52.000000', 'Nguyễn Ngọc Sang', NULL, NULL, NULL, 'FEMALE', '0916333333', 'https://cdn.discordapp.com/attachments/1168144426141499412/1179940650066653285/fried-chicken.png?ex=657b9c5d&is=6569275d&hm=0ae4a9a679ee8950ff308033178661b42f948305c6831656315da547afc97af7&', NULL, 'LOCAL'), +(0x57806cf639fa32458a3d066714f57ab2, 'couplenamjin520@gmail.com', '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', '2003-11-15', 3, b'0', '2023-11-11 13:56:19.000000', 'Nguyễn Yến Vy', NULL, NULL, NULL, 'FEMALE', '0902991511', 'https://cdn.discordapp.com/attachments/1168144426141499412/1169264779769155594/c6e56503cfdd87da299f72dc416023d4.jpg?ex=6554c5af&is=654250af&hm=440ebc745e441103a1824a80edb22f2d1c371743e8d7a4d413dbbc62eae31554&', NULL, 'LOCAL'), +(0x58de8aef2b933db6b210173e93e78674, 'vominhhoang044@gmai', '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', NULL, 3, b'0', '2023-12-01 00:37:49.636937', 'asdasd', NULL, NULL, NULL, 'UNKNOWN', NULL, 'https://cdn.discordapp.com/attachments/1168144426141499412/1169264779769155594/c6e56503cfdd87da299f72dc416023d4.jpg?ex=6554c5af&is=654250af&hm=440ebc745e441103a1824a80edb22f2d1c371743e8d7a4d413dbbc62eae31554&', NULL, 'LOCAL'), +(0x6870e57db7e04714aecfe9163e417b9d, 'temp.duynguyen@gmail.com', '123', NULL, NULL, NULL, '2024-06-14 14:47:58.549721', 'Duy Nguyễn', NULL, NULL, NULL, 'UNKNOWN', NULL, 'https://lh3.googleusercontent.com/a/ACg8ocLLZyt5YJmPPsJ8ep8G_xFE_ekJwCUD9Q0ivrKNWQPO9qKQhQ=s96-c', 'google', 'GOOGLE'), +(0x76d3c946d2d732249952c6fddb4526a2, 'vmhoafng@gmail.com', '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', '2023-11-28', 3, b'0', '2023-12-01 01:49:08.239620', 'vmhoafng', NULL, NULL, NULL, 'FEMALE', '1231231231', 'https://cdn.discordapp.com/attachments/1159668660340789259/1179901739789463642/IMG_1657.jpeg?ex=657b7820&is=65690320&hm=d76ee3665b774fd4017eea3f149b0fd4ecebcdf5828116a153b5329b23d6983c&', NULL, 'LOCAL'), +(0x76ea668f6c6b33bd8a9925f7eac2becf, 'admin@gmail.com', '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', NULL, 1, b'1', '2023-11-23 14:19:45.829821', 'admin', NULL, NULL, NULL, 'UNKNOWN', NULL, 'https://cdn.discordapp.com/attachments/1168144426141499412/1169264779769155594/c6e56503cfdd87da299f72dc416023d4.jpg?ex=6554c5af&is=654250af&hm=440ebc745e441103a1824a80edb22f2d1c371743e8d7a4d413dbbc62eae31554&', NULL, 'LOCAL'), +(0x9028e62088533b129d6582da9585bc8a, 'datnguyenthanh1605@gmail.com', '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', NULL, 2, b'1', '2023-11-22 12:28:43.561620', 'Thành Đạt Nguyễn', NULL, NULL, NULL, 'UNKNOWN', NULL, 'https://lh3.googleusercontent.com/a/ACg8ocLQ2DimcTxOgv7Z2vu39ZnwfeqMlkKqyBZHgnEUsCYHAxE=s96-c', NULL, 'LOCAL'), +(0x9400d33d87e1389b8472113ae6869c79, 'webcamffff@gmail.com', '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', NULL, 3, b'0', '2023-12-02 00:01:36.089398', 'Con Cặc Chó Sơn', NULL, NULL, NULL, 'UNKNOWN', NULL, 'https://cdn.discordapp.com/attachments/1168144426141499412/1169264779769155594/c6e56503cfdd87da299f72dc416023d4.jpg?ex=6554c5af&is=654250af&hm=440ebc745e441103a1824a80edb22f2d1c371743e8d7a4d413dbbc62eae31554&', NULL, 'LOCAL'), +(0x97913044f5843cdfaaccf6a41ee91bb3, 'vominhhoang044@gmail.com', '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', '2003-07-31', 2, b'1', '2023-11-23 13:51:06.169499', 'vmhoafng', NULL, NULL, NULL, 'MALE', '0929829783', 'https://lh3.googleusercontent.com/a/ACg8ocIlPJbedl_pdcvikbIpQZjPy5Yx75TqSZ1V1ElCgl4UkvGq=s96-c', NULL, 'LOCAL'), +(0xd733ff2ae4c6338b8a27c8d80791b5ea, 'datnt.653@gmail.com', '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', NULL, 3, b'0', '2023-12-01 00:50:36.195262', 'nguyen thanh dat', NULL, NULL, NULL, 'UNKNOWN', NULL, 'https://cdn.discordapp.com/attachments/1168144426141499412/1169264779769155594/c6e56503cfdd87da299f72dc416023d4.jpg?ex=6554c5af&is=654250af&hm=440ebc745e441103a1824a80edb22f2d1c371743e8d7a4d413dbbc62eae31554&', NULL, 'LOCAL'), +(0xe01a333337584090a7158a1359c1b29d, 'thanhduy191103@gmail.com', '123', NULL, NULL, b'1', '2024-05-30 16:58:05.103126', 'Nguyễn Thanh Duy', NULL, NULL, NULL, 'UNKNOWN', NULL, 'https://lh3.googleusercontent.com/a/ACg8ocL1CMdRQBanZL6P2EwxKFD6mdE4nr1_tZmPZ8qicUw5p8bDbw=s96-c', 'google', 'GOOGLE'), +(0xe33ffe0f51753ab0997167196c3fa9dc, 'hn26677@gmail.com', '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', '2003-11-10', 3, b'0', '2023-11-09 15:06:54.000000', 'Hoang ne', NULL, NULL, NULL, 'UNKNOWN', '09169211111', 'https://cdn.discordapp.com/attachments/1159668660340789259/1177446234722680893/firefox2.jpg?ex=65728942&is=65601442&hm=85442a15f481672359b9af9fe635da65cde8932cf3dfd3a9238c68f97262acbe&', NULL, 'LOCAL'), +(0xfc51f2bf983f31568043c339b41c4431, 'hn266222277@gmail.com', '$2a$10$iCIcjsW9T6r0havQ1gTLSua3zKSX6vEOW2P6ySNBuivVpID3ya152', NULL, 3, b'0', '2023-12-01 00:51:37.739730', 'Son So', NULL, NULL, NULL, 'UNKNOWN', NULL, 'https://cdn.discordapp.com/attachments/1168144426141499412/1169264779769155594/c6e56503cfdd87da299f72dc416023d4.jpg?ex=6554c5af&is=654250af&hm=440ebc745e441103a1824a80edb22f2d1c371743e8d7a4d413dbbc62eae31554&', NULL, 'LOCAL'); +SET FOREIGN_KEY_CHECKS=1; +COMMIT; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/src/test/resources/db/schema.sql b/src/test/resources/db/schema.sql new file mode 100644 index 0000000..6d34865 --- /dev/null +++ b/src/test/resources/db/schema.sql @@ -0,0 +1,649 @@ +-- phpMyAdmin SQL Dump +-- version 5.2.1 +-- https://www.phpmyadmin.net/ +-- +-- Host: localhost:3306 +-- Generation Time: Jul 10, 2024 at 09:08 PM +-- Server version: 10.6.17-MariaDB +-- PHP Version: 8.1.28 + +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +START TRANSACTION; +SET time_zone = "+00:00"; + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; + +-- +-- Database: `db_dev` +-- + +-- -------------------------------------------------------- + +-- +-- Table structure for table `bills` +-- +use db_dev; +CREATE TABLE `bills` ( + `id` binary(16) NOT NULL, + `payment_at` datetime(6) DEFAULT NULL, + `total` double DEFAULT NULL, + `user_id` binary(16) NOT NULL, + `transaction_id` varchar(255) DEFAULT NULL, + `change_point` int(11) DEFAULT NULL, + `cancel_reason` varchar(255) DEFAULT NULL, + `status` tinyint(4) NOT NULL, + `create_time` datetime(6) DEFAULT NULL, + `cancel_date` datetime(6) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `bill_status` +-- + +CREATE TABLE `bill_status` ( + `id` int(11) NOT NULL, + `name` varchar(255) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `cinemas` +-- + +CREATE TABLE `cinemas` ( + `id` int(10) UNSIGNED NOT NULL, + `address` varchar(255) NOT NULL, + `city` varchar(255) NOT NULL, + `district` varchar(255) NOT NULL, + `name` varchar(255) NOT NULL, + `description` text DEFAULT NULL, + `phone_number` varchar(255) NOT NULL, + `status` enum('CLOSED','MAINTAINED','OPENING') NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `cinemas_seq` +-- + +CREATE TABLE `cinemas_seq` ( + `next_val` bigint(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `customer_order` +-- + +CREATE TABLE `customer_order` ( + `id` bigint(20) NOT NULL, + `description` varchar(255) DEFAULT NULL, + `status` tinyint(4) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `customer_order_seq` +-- + +CREATE TABLE `customer_order_seq` ( + `next_val` bigint(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `movies` +-- + +CREATE TABLE `movies` ( + `id` int(10) UNSIGNED NOT NULL, + `code` varchar(255) NOT NULL, + `caster` varchar(255) DEFAULT NULL, + `description` text DEFAULT NULL, + `director` varchar(255) DEFAULT NULL, + `language` varchar(255) DEFAULT NULL, + `name` varchar(255) DEFAULT NULL, + `sub_name` varchar(255) DEFAULT NULL, + `number_of_ratings` int(11) DEFAULT NULL, + `poster` varchar(255) DEFAULT NULL, + `rated` int(10) UNSIGNED DEFAULT NULL, + `release_date` date DEFAULT NULL, + `running_time` int(11) DEFAULT NULL, + `slug` varchar(255) DEFAULT NULL, + `trailer` varchar(255) DEFAULT NULL, + `end_date` date DEFAULT NULL, + `horizontal_poster` varchar(255) DEFAULT NULL, + `sum_of_ratings` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `movies_images` +-- + +CREATE TABLE `movies_images` ( + `id` int(10) UNSIGNED NOT NULL, + `extension` varchar(255) DEFAULT NULL, + `path` varchar(255) DEFAULT NULL, + `movie_id` int(10) UNSIGNED NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `movies_seq` +-- + +CREATE TABLE `movies_seq` ( + `next_val` bigint(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `movie_genre` +-- + +CREATE TABLE `movie_genre` ( + `movie_genre_id` int(10) UNSIGNED NOT NULL, + `movie_id` int(10) UNSIGNED NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `movie_genres` +-- + +CREATE TABLE `movie_genres` ( + `id` int(10) UNSIGNED NOT NULL, + `name` varchar(250) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `movie_genres_seq` +-- + +CREATE TABLE `movie_genres_seq` ( + `next_val` bigint(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `permissions` +-- + +CREATE TABLE `permissions` ( + `id` int(11) NOT NULL, + `name` varchar(255) NOT NULL, + `decription` varchar(255) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `permissions_seq` +-- + +CREATE TABLE `permissions_seq` ( + `next_val` bigint(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `roles` +-- + +CREATE TABLE `roles` ( + `id` int(11) NOT NULL, + `name` varchar(255) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `roles_seq` +-- + +CREATE TABLE `roles_seq` ( + `next_val` bigint(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `roles_users` +-- + +CREATE TABLE `roles_users` ( + `role_id` int(11) NOT NULL, + `users_id` binary(16) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `role_permission` +-- + +CREATE TABLE `role_permission` ( + `role_id` int(11) NOT NULL, + `permission_id` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `rooms` +-- + +CREATE TABLE `rooms` ( + `id` int(10) UNSIGNED NOT NULL, + `code` varchar(255) NOT NULL, + `available_seats` int(11) DEFAULT NULL, + `name` varchar(255) DEFAULT NULL, + `total_seats` int(11) DEFAULT NULL, + `cinema_id` int(10) UNSIGNED NOT NULL, + `slug` varchar(255) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `rooms_seq` +-- + +CREATE TABLE `rooms_seq` ( + `next_val` bigint(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `seats` +-- + +CREATE TABLE `seats` ( + `id` int(11) NOT NULL, + `row_name` varchar(255) DEFAULT NULL, + `row_index` int(11) DEFAULT NULL, + `status` tinyint(1) DEFAULT 1, + `room_id` int(10) UNSIGNED NOT NULL, + `seat_type_id` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `seats_seq` +-- + +CREATE TABLE `seats_seq` ( + `next_val` bigint(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `seat_types` +-- + +CREATE TABLE `seat_types` ( + `id` int(11) NOT NULL, + `name` varchar(255) NOT NULL, + `price` double UNSIGNED NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `seat_types_seq` +-- + +CREATE TABLE `seat_types_seq` ( + `next_val` bigint(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `showtimes` +-- + +CREATE TABLE `showtimes` ( + `id` int(10) UNSIGNED NOT NULL, + `running_time` int(11) DEFAULT NULL, + `start_date` date NOT NULL, + `start_time` time(6) NOT NULL, + `status` tinyint(1) DEFAULT 1, + `room_id` int(10) UNSIGNED NOT NULL DEFAULT 0, + `movie_id` int(10) UNSIGNED DEFAULT NULL, + `code` varchar(50) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `showtimes_seq` +-- + +CREATE TABLE `showtimes_seq` ( + `next_val` bigint(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `tickets` +-- + +CREATE TABLE `tickets` ( + `id` binary(16) NOT NULL, + `bill_id` binary(16) DEFAULT NULL, + `showtime_id` int(10) UNSIGNED NOT NULL, + `seat_id` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `users` +-- + +CREATE TABLE `users` ( + `id` binary(16) NOT NULL, + `email` varchar(255) NOT NULL, + `password` varchar(255) NOT NULL, + `date_of_birth` date DEFAULT NULL, + `role_id` int(11) DEFAULT NULL, + `verify` bit(1) DEFAULT NULL, + `create_date` datetime(6) DEFAULT NULL, + `full_name` varchar(255) NOT NULL, + `verify_account` varchar(255) DEFAULT NULL, + `verify_mail` varchar(255) DEFAULT NULL, + `verify_pass` varchar(255) DEFAULT NULL, + `gender` enum('FEMALE','MALE','UNKNOWN') NOT NULL, + `phone_number` varchar(255) DEFAULT NULL, + `avatar` varchar(255) DEFAULT NULL, + `provider_id` varchar(255) DEFAULT NULL, + `provider` enum('LOCAL','GOOGLE') NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +-- +-- Indexes for dumped tables +-- + +-- +-- Indexes for table `bills` +-- +ALTER TABLE `bills` + ADD PRIMARY KEY (`id`), + ADD KEY `FK_bills_users` (`user_id`), + ADD KEY `FK7s2dh8hdb6rthogn42qb1a3aw` (`status`) USING BTREE; + +-- +-- Indexes for table `bill_status` +-- +ALTER TABLE `bill_status` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- Indexes for table `cinemas` +-- +ALTER TABLE `cinemas` + ADD PRIMARY KEY (`id`); + +-- +-- Indexes for table `customer_order` +-- +ALTER TABLE `customer_order` + ADD PRIMARY KEY (`id`); + +-- +-- Indexes for table `movies` +-- +ALTER TABLE `movies` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `movie_id` (`code`); + +-- +-- Indexes for table `movies_images` +-- +ALTER TABLE `movies_images` + ADD PRIMARY KEY (`id`) USING BTREE, + ADD KEY `FK_movies_images_movies` (`movie_id`); + +-- +-- Indexes for table `movie_genre` +-- +ALTER TABLE `movie_genre` + ADD PRIMARY KEY (`movie_genre_id`,`movie_id`) USING BTREE, + ADD KEY `FKg7f38h6umffo51no9ywq91438` (`movie_id`), + ADD KEY `FK87qryjrya5r6wn0su5fl0ynu6` (`movie_genre_id`) USING BTREE; + +-- +-- Indexes for table `movie_genres` +-- +ALTER TABLE `movie_genres` + ADD PRIMARY KEY (`id`); + +-- +-- Indexes for table `permissions` +-- +ALTER TABLE `permissions` + ADD PRIMARY KEY (`id`); + +-- +-- Indexes for table `roles` +-- +ALTER TABLE `roles` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- Indexes for table `roles_users` +-- +ALTER TABLE `roles_users` + ADD PRIMARY KEY (`role_id`,`users_id`), + ADD UNIQUE KEY `UK_snx09g5kxfrdi207cl1jhjmev` (`users_id`); + +-- +-- Indexes for table `role_permission` +-- +ALTER TABLE `role_permission` + ADD PRIMARY KEY (`role_id`,`permission_id`), + ADD KEY `FK2xn8qv4vw30i04xdxrpvn3bdi` (`permission_id`); + +-- +-- Indexes for table `rooms` +-- +ALTER TABLE `rooms` + ADD PRIMARY KEY (`id`), + ADD KEY `room_cinema` (`cinema_id`); + +-- +-- Indexes for table `seats` +-- +ALTER TABLE `seats` + ADD PRIMARY KEY (`id`) USING BTREE, + ADD KEY `FKfu2c510l2cl8lblj3x3x4o0vl` (`room_id`), + ADD KEY `FK3cwtx4s7f8dbcrghl8uv2s08d` (`seat_type_id`); + +-- +-- Indexes for table `seat_types` +-- +ALTER TABLE `seat_types` + ADD PRIMARY KEY (`id`) USING BTREE; + +-- +-- Indexes for table `showtimes` +-- +ALTER TABLE `showtimes` + ADD PRIMARY KEY (`id`), + ADD KEY `FKrumrrbei9jppryk4teoyoetit` (`room_id`), + ADD KEY `FKeltpyuei1d5g3n6ikpsjwwil6` (`movie_id`); + +-- +-- Indexes for table `tickets` +-- +ALTER TABLE `tickets` + ADD PRIMARY KEY (`id`), + ADD KEY `FKo0u22315eoxdv59tn6wsdn8b1` (`showtime_id`), + ADD KEY `FK4qm1s4orf1onvmo4y5qsbl5mv` (`seat_id`) USING BTREE, + ADD KEY `FKaqpjta99er6ahhyyq5689cyw4` (`bill_id`); + +-- +-- Indexes for table `users` +-- +ALTER TABLE `users` + ADD PRIMARY KEY (`id`), + ADD KEY `FKp56c1712k691lhsyewcssf40f` (`role_id`); + +-- +-- AUTO_INCREMENT for dumped tables +-- + +-- +-- AUTO_INCREMENT for table `bill_status` +-- +ALTER TABLE `bill_status` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT for table `cinemas` +-- +ALTER TABLE `cinemas` + MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT for table `movies` +-- +ALTER TABLE `movies` + MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT for table `movies_images` +-- +ALTER TABLE `movies_images` + MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT for table `roles` +-- +ALTER TABLE `roles` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT for table `rooms` +-- +ALTER TABLE `rooms` + MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT for table `seats` +-- +ALTER TABLE `seats` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT for table `seat_types` +-- +ALTER TABLE `seat_types` + MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; + +-- +-- AUTO_INCREMENT for table `showtimes` +-- +ALTER TABLE `showtimes` + MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT; + +-- +-- Constraints for dumped tables +-- + +-- +-- Constraints for table `bills` +-- +ALTER TABLE `bills` + ADD CONSTRAINT `FK_bills_users` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`); + +-- +-- Constraints for table `movies_images` +-- +ALTER TABLE `movies_images` + ADD CONSTRAINT `FK_movies_images_movies` FOREIGN KEY (`movie_id`) REFERENCES `movies` (`id`); + +-- +-- Constraints for table `movie_genre` +-- +ALTER TABLE `movie_genre` + ADD CONSTRAINT `FK87qryjrya5r6wn0su5fl0ynu6` FOREIGN KEY (`movie_genre_id`) REFERENCES `movie_genres` (`id`), + ADD CONSTRAINT `FKg7f38h6umffo51no9ywq91438` FOREIGN KEY (`movie_id`) REFERENCES `movies` (`id`); + +-- +-- Constraints for table `roles_users` +-- +ALTER TABLE `roles_users` + ADD CONSTRAINT `FK4glr8k8swy5nti6n5x35ofucj` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`), + ADD CONSTRAINT `FKsmos02hm32191ogexm2ljik9x` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`); + +-- +-- Constraints for table `role_permission` +-- +ALTER TABLE `role_permission` + ADD CONSTRAINT `FK2xn8qv4vw30i04xdxrpvn3bdi` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`), + ADD CONSTRAINT `FKtfgq8q9blrp0pt1pvggyli3v9` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`); + +-- +-- Constraints for table `rooms` +-- +ALTER TABLE `rooms` + ADD CONSTRAINT `room_cinema` FOREIGN KEY (`cinema_id`) REFERENCES `cinemas` (`id`); + +-- +-- Constraints for table `seats` +-- +ALTER TABLE `seats` + ADD CONSTRAINT `FK3cwtx4s7f8dbcrghl8uv2s08d` FOREIGN KEY (`seat_type_id`) REFERENCES `seat_types` (`id`), + ADD CONSTRAINT `FKg993pi7ucgy616icmddq8u335` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`); + +-- +-- Constraints for table `showtimes` +-- +ALTER TABLE `showtimes` + ADD CONSTRAINT `FKeltpyuei1d5g3n6ikpsjwwil6` FOREIGN KEY (`movie_id`) REFERENCES `movies` (`id`), + ADD CONSTRAINT `FKrumrrbei9jppryk4teoyoetit` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`id`); + +-- +-- Constraints for table `tickets` +-- +ALTER TABLE `tickets` + ADD CONSTRAINT `FK4qm1s4orf1onvmo4y5qsbl5mv` FOREIGN KEY (`seat_id`) REFERENCES `seats` (`id`), + ADD CONSTRAINT `FKaqpjta99er6ahhyyq5689cyw4` FOREIGN KEY (`bill_id`) REFERENCES `bills` (`id`) ON DELETE CASCADE, + ADD CONSTRAINT `FKo0u22315eoxdv59tn6wsdn8b1` FOREIGN KEY (`showtime_id`) REFERENCES `showtimes` (`id`); + +-- +-- Constraints for table `users` +-- +ALTER TABLE `users` + ADD CONSTRAINT `FKp56c1712k691lhsyewcssf40f` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`); +COMMIT; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; diff --git a/src/test/resources/showtime.sql b/src/test/resources/showtime.sql new file mode 100644 index 0000000..79f48e4 --- /dev/null +++ b/src/test/resources/showtime.sql @@ -0,0 +1,26 @@ +INSERT INTO movies (id, code, caster, description, director, language, name, sub_name, number_of_ratings, poster, rated, + release_date, running_time, slug, trailer, end_date, horizontal_poster, sum_of_ratings) +VALUES (1, 'mv-0001', 'Greta Lee, Teo Yoo', + 'Muôn Kiếp Nhân Duyên xoay quanh hai nhân vật chính - Nora (Greta Lee) và Hae Sung (Teo Yoo). Tình bạn thân thiết của họ bị chia cắt khi Nora theo gia đình di cư khỏi Hàn Quốc vào năm 12 tuổi. 20 năm sau, như một mối duyên tiền định, họ gặp lại nhau tại Mỹ', + 'Celine', 'Tiếng Anh', 'Past Lives', 'Muôn Kiếp Nhân Duyên', 3, + 'https://cdn.discordapp.com/attachments/1159668660340789259/1169947106191093840/both-water.png?ex=65574126&is=6544cc26&hm=033c5de326692af06130dcc0e32cc684a441d99a98eecdc1c8e90cc028c27727&', + 13, '2023-10-10', 106, 'past-lives', 'https://www.youtube.com/watch?v=lBdLBY249Do', '2023-11-25', + 'https://cdn.discordapp.com/attachments/1159668660340789259/1170792832735387688/2-ga-ran.png?ex=655a54cb&is=6547dfcb&hm=9a0fcbd579af5a185b42278b807c3c3658a38c89ec598febf2d880daae1eb599&', + 25); + + +INSERT INTO cinemas (id, address, city, district, name, description, phone_number, status) +VALUES (1, '466', 'HCM', '122', 'Galaxy Trung Chanh', + 'Hệ thống rạp The Cinema được thiết kế sang trọng và tiện nghi. Khách hàng bước vào không gian rạp sẽ cảm nhận ngay sự thoải mái và tiện lợi từ việc sắp xếp ghế ngồi theo lối kiến trúc hiện đại, tạo nên không gian xem phim tuyệt vời. Mỗi phòng chiếu được trang bị hệ thống âm thanh vòm và màn hình siêu nét, mang đến trải nghiệm hình ảnh và âm thanh sống động, hấp dẫn.', + '091222', 'OPENING'); + +INSERT INTO rooms (id, available_seats, name, total_seats, cinema_id, slug) +VALUES (1, 150, 'RAP 1-Trung Chánh', 150, 1, NULL); +INSERT INTO rooms (id, available_seats, name, total_seats, cinema_id, slug) +VALUES (2, 150, 'RAP 2-Trung Chánh', 150, 1, NULL); + +INSERT INTO showtimes (id, running_time, start_date, start_time, status, room_id, movie_id) +VALUES (1, 140, '2024-06-19', '11:14:05.000000', 1, 1, 1); +INSERT INTO showtimes (id, running_time, start_date, start_time, status, room_id, movie_id) +VALUES (2, 140, '2024-06-19', '11:14:05.000000', 2, 1, 1); +