From 0502efe294704d8ac48f10db19b489ad100c5cd2 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Mon, 20 May 2024 00:00:23 +0900 Subject: [PATCH 01/38] =?UTF-8?q?entity=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/purchase/PurchaseItem.java | 61 +++++++++++++++++++ .../repository/PurchaseItemRepository.java | 12 ++++ .../Tiketeer/domain/ticket/Ticket.java | 24 ++++++-- .../Tiketeer/domain/ticketing/Ticketing.java | 7 +-- 4 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepository.java diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java new file mode 100644 index 00000000..19ed1efb --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java @@ -0,0 +1,61 @@ +package com.tiketeer.Tiketeer.domain.purchase; + +import java.time.LocalDateTime; +import java.util.UUID; + +import org.hibernate.annotations.UuidGenerator; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import com.tiketeer.Tiketeer.domain.ticket.Ticket; + +import jakarta.persistence.Column; +import jakarta.persistence.ConstraintMode; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "purchase_items") +@EntityListeners(AuditingEntityListener.class) +@Getter +@EqualsAndHashCode +@ToString +public class PurchaseItem { + @Id + @UuidGenerator + @Column(name = "purchase_item_id", nullable = false, updatable = false) + private UUID id; + + @ManyToOne + @JoinColumn(name = "purchase_id", foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT)) + private Purchase purchase; + + @ManyToOne + @JoinColumn(name = "ticket_id", foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT)) + private Ticket ticket; + + @CreatedDate + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt; + + @Builder + public PurchaseItem(Purchase purchase, Ticket ticket) { + this.purchase = purchase; + this.ticket = ticket; + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepository.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepository.java new file mode 100644 index 00000000..b2c2640a --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepository.java @@ -0,0 +1,12 @@ +package com.tiketeer.Tiketeer.domain.purchase.repository; + +import java.util.UUID; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.tiketeer.Tiketeer.domain.purchase.Purchase; + +@Repository +public interface PurchaseItemRepository extends JpaRepository { +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java index a4c82be3..c60a7859 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java @@ -7,7 +7,6 @@ import org.springframework.data.annotation.CreatedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import com.tiketeer.Tiketeer.domain.purchase.Purchase; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import jakarta.persistence.Column; @@ -41,10 +40,21 @@ public class Ticket { @Column(name = "ticket_id", nullable = false, updatable = false) private UUID id; - @ManyToOne - @JoinColumn(name = "purchase_id", foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT)) @Setter - private Purchase purchase; + @Column(name = "price", nullable = false) + private long price; + + @Setter + @Column(name = "stock", nullable = false) + private Integer stock; + + @Setter + @Column(name = "remaining_stock", nullable = false) + private Integer remainingStock; + + @Setter + @Column(name = "description", columnDefinition = "TEXT") + private String description; @ManyToOne @JoinColumn(name = "ticketing_id", foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT)) @@ -59,8 +69,10 @@ public class Ticket { private Integer version; @Builder - public Ticket(Purchase purchase, Ticketing ticketing) { - this.purchase = purchase; + public Ticket(long price, int stock, int remainingStock, Ticketing ticketing) { + this.price = price; + this.stock = stock; + this.remainingStock = remainingStock; this.ticketing = ticketing; } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/Ticketing.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/Ticketing.java index 573f37c1..c42ce907 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/Ticketing.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/Ticketing.java @@ -43,10 +43,6 @@ public class Ticketing { @Column(name = "ticketing_id", nullable = false, updatable = false) private UUID id; - @Setter - @Column(name = "price", nullable = false) - private long price; - @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "owner_id", referencedColumnName = "member_id", foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT)) @@ -104,14 +100,13 @@ public class Ticketing { @Builder public Ticketing( - long price, Member member, + Member member, String description, String title, String location, LocalDateTime eventTime, String category, int runningMinutes, LocalDateTime saleStart, LocalDateTime saleEnd, String thumbnailPath, StorageEnum storageEnum ) { - this.price = price; this.member = member; this.description = description; this.title = title; From 6225c1ebbbc8e6a287070b978ec422e5b227b9fe Mon Sep 17 00:00:00 2001 From: dla0510 Date: Mon, 20 May 2024 00:00:56 +0900 Subject: [PATCH 02/38] =?UTF-8?q?flyway=20migration=20sql=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/db/migration/V2__migration.sql | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/resources/db/migration/V2__migration.sql diff --git a/src/main/resources/db/migration/V2__migration.sql b/src/main/resources/db/migration/V2__migration.sql new file mode 100644 index 00000000..d27c8fca --- /dev/null +++ b/src/main/resources/db/migration/V2__migration.sql @@ -0,0 +1,25 @@ +ALTER TABLE ticketings +DROP COLUMN price; + +ALTER TABLE tickets +DROP COLUMN purchase_id; + +ALTER TABLE tickets +ADD COLUMN price bigint not null; + +ALTER TABLE tickets +ADD COLUMN stock INTEGER not null; + +ALTER TABLE tickets +ADD COLUMN remaining_stock INTEGER not null; + +ALTER TABLE tickets +ADD COLUMN description TEXT; + +create table purchase_items ( + created_at timestamp(6) not null, + purchase_item_id binary(16) not null, + purchase_id binary(16) not null, + ticket_id binary(16) not null, + primary key (purchase_item_id) +); \ No newline at end of file From aee82370f772881be18e72edafb59775c7b28eab Mon Sep 17 00:00:00 2001 From: dla0510 Date: Mon, 20 May 2024 00:13:04 +0900 Subject: [PATCH 03/38] =?UTF-8?q?build=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/PurchaseRepository.java | 6 +- .../purchase/service/PurchaseService.java | 6 +- .../usecase/CreatePurchaseUseCaseCore.java | 10 +-- .../usecase/DeletePurchaseTicketsUseCase.java | 20 +---- .../usecase/GetPurchaseTicketsUseCase.java | 2 +- .../decorator/CreatePurchaseUseCaseOLock.java | 2 +- .../decorator/CreatePurchaseUseCasePLock.java | 2 +- .../ticket/repository/TicketRepository.java | 17 ++-- .../repository/TicketingRepository.java | 18 ++--- .../usecase/CreateTicketingUseCase.java | 11 +-- .../usecase/UpdateTicketingUseCase.java | 1 - .../controller/MemberControllerTest.java | 19 ++--- .../service/MemberTicketingServiceTest.java | 7 +- .../GetMemberPurchasesUseCaseTest.java | 10 +-- .../domain/purchase/PurchaseTestHelper.java | 3 +- .../repository/PurchaseRepositoryTest.java | 10 +-- .../purchase/service/PurchaseServiceTest.java | 6 +- .../CreatePurchaseConcurrencyTest.java | 1 - ...UseCaseDistributedLockConcurrencyTest.java | 15 +--- ...eUseCaseOptimisticLockConcurrencyTest.java | 15 +--- ...UseCasePessimisticLockConcurrencyTest.java | 15 +--- .../usecase/CreatePurchaseUseCaseTest.java | 3 +- .../DeletePurchaseTicketsUseCaseTest.java | 8 +- .../repository/TicketRepositoryTest.java | 80 +------------------ .../ticket/service/TicketCrudServiceTest.java | 1 - .../domain/ticketing/TicketingTestHelper.java | 1 - .../controller/TicketingControllerTest.java | 5 +- .../repository/TicketingRepositoryTest.java | 18 ++--- .../service/TicketingServiceTest.java | 9 +-- .../service/TicketingStockServiceTest.java | 1 - .../usecase/GetAllTicketingsUseCaseTest.java | 3 +- .../usecase/GetTicketingUseCaseTest.java | 4 +- .../usecase/UpdateTicketingUseCaseTest.java | 1 - .../Tiketeer/testhelper/TestHelperTest.java | 3 +- 34 files changed, 73 insertions(+), 260 deletions(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepository.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepository.java index 79e7cff2..d2f4af86 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepository.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepository.java @@ -15,9 +15,9 @@ @Repository public interface PurchaseRepository extends JpaRepository { @Query( - "SELECT new com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesResultDto(p.id, tg.id, tg.title, tg.location, tg.eventTime, tg.saleStart, tg.saleEnd, p.createdAt, tg.category, tg.price, count(*)) " - + "FROM Purchase p LEFT JOIN Ticket t ON p = t.purchase LEFT JOIN Ticketing tg ON t.ticketing = tg " - + "WHERE p.member = :member GROUP BY p, tg ORDER BY p.createdAt, p.id" + "SELECT new com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesResultDto(p.id, tg.id, tg.title, tg.location, tg.eventTime, tg.saleStart, tg.saleEnd, p.createdAt, tg.category, sum(t.price), count(*)) " + + "FROM Purchase p LEFT JOIN PurchaseItem pi ON p = pi.purchase LEFT JOIN Ticket t ON t = pi.ticket LEFT JOIN Ticketing tg ON t.ticketing = tg " + + "WHERE p.member = :member GROUP BY p, t ORDER BY p.createdAt, p.id" ) List findWithTicketingByMember(@Param("member") Member member); } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseService.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseService.java index 7e903c93..28318d7f 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseService.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseService.java @@ -9,7 +9,6 @@ import org.springframework.transaction.annotation.Transactional; import com.tiketeer.Tiketeer.domain.purchase.exception.AccessForNotOwnedPurchaseException; -import com.tiketeer.Tiketeer.domain.purchase.exception.EmptyPurchaseException; import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotFoundException; import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotInSalePeriodException; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; @@ -52,10 +51,7 @@ public void validatePurchaseOwnership(UUID purchaseId, String email) { public List findTicketsUnderPurchase(UUID purchaseId) { var purchase = purchaseRepository.findById(purchaseId).orElseThrow(PurchaseNotFoundException::new); - var ticketsUnderPurchase = ticketRepository.findAllByPurchase(purchase); - if (ticketsUnderPurchase.isEmpty()) { - throw new EmptyPurchaseException(); - } + var ticketsUnderPurchase = ticketRepository.findAll(); return ticketsUnderPurchase; } } \ No newline at end of file diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java index e6d10e56..c869ada8 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java @@ -45,7 +45,7 @@ public CreatePurchaseUseCaseCore( @Override @Transactional public CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command) { - return execPurchaseLogic(command, ticketRepository::findByTicketingIdAndPurchaseIsNullOrderById); + return execPurchaseLogic(command, ticketRepository::findByTicketingIdOrderById); } @Override @@ -66,20 +66,16 @@ private CreatePurchaseResultDto execPurchaseLogic(CreatePurchaseCommandDto comma var ticketing = ticketingService.findById(ticketingId); - memberPointService.subtractPoint(member.getId(), ticketing.getPrice() * count); - var purchase = purchaseRepository.save(Purchase.builder().member(member).build()); var tickets = listTicketStrategy.findByTicketingIdAndPurchaseIsNullOrderById(ticketingId, Limit.of(count)); + memberPointService.subtractPoint(member.getId(), tickets.getFirst().getPrice() * count); + if (tickets.size() < count) { throw new NotEnoughTicketException(); } - tickets.forEach(ticket -> { - ticket.setPurchase(purchase); - }); - return CreatePurchaseResultDto.builder() .purchaseId(purchase.getId()) .createdAt(purchase.getCreatedAt()) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCase.java index 227ea027..ad8973de 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCase.java @@ -1,7 +1,5 @@ package com.tiketeer.Tiketeer.domain.purchase.usecase; -import java.util.concurrent.atomic.AtomicInteger; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -12,7 +10,6 @@ import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.purchase.service.PurchaseService; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.DeletePurchaseTicketsCommandDto; -import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; @Service @@ -38,26 +35,11 @@ public class DeletePurchaseTicketsUseCase { public void deletePurchaseTickets(DeletePurchaseTicketsCommandDto command) { var purchase = purchaseRepository.findById(command.getPurchaseId()).orElseThrow( PurchaseNotFoundException::new); - var ticketsUnderPurchase = purchaseService.findTicketsUnderPurchase(purchase.getId()); var ticketsToRefund = ticketRepository.findAllById(command.getTicketIds()); - var ticketing = ticketsUnderPurchase.getFirst().getTicketing(); purchaseService.validatePurchaseOwnership(purchase.getId(), command.getMemberEmail()); - purchaseService.validateTicketingSalePeriod(ticketing.getId(), command.getCommandCreatedAt()); - var ticketIdUnderPurchase = ticketsUnderPurchase.stream().map(Ticket::getId).toList(); - AtomicInteger numOfDeletedTicket = new AtomicInteger(); - ticketsToRefund.forEach(ticket -> { - if (ticketIdUnderPurchase.contains(ticket.getId())) { - ticket.setPurchase(null); - numOfDeletedTicket.getAndIncrement(); - } - }); - if (numOfDeletedTicket.get() == ticketsUnderPurchase.size()) { - purchaseRepository.delete(purchase); - } - var member = memberCrudService.findByEmail(command.getMemberEmail()); - memberPointService.addPoint(member.getId(), ticketing.getPrice() * numOfDeletedTicket.get()); + memberPointService.addPoint(member.getId(), ticketsToRefund.getFirst().getPrice()); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/GetPurchaseTicketsUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/GetPurchaseTicketsUseCase.java index d7d01c57..3b8bbcf9 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/GetPurchaseTicketsUseCase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/GetPurchaseTicketsUseCase.java @@ -30,7 +30,7 @@ public GetPurchaseTicketsResultDto getPurchaseTickets(GetPurchaseTicketsCommandD purchaseService.validatePurchaseOwnership(command.getPurchaseId(), command.getMemberEmail()); var purchase = purchaseRepository.findById(command.getPurchaseId()).orElseThrow(PurchaseNotFoundException::new); - var tickets = ticketRepository.findAllByPurchase(purchase); + var tickets = ticketRepository.findAll(); return GetPurchaseTicketsResultDto.builder() .ticketIds(tickets.stream().map(Ticket::getId).toList()) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCaseOLock.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCaseOLock.java index 4f2100e5..73a98012 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCaseOLock.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCaseOLock.java @@ -31,6 +31,6 @@ public CreatePurchaseUseCaseOLock(CreatePurchaseUseCase createPurchaseUseCase, ) public CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command) { return innerUseCase.createPurchase(command, - ticketRepository::findByTicketingIdAndPurchaseIsNullOrderByIdWithOptimisticLock); + ticketRepository::findByTicketingIdOrderByIdWithOptimisticLock); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCasePLock.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCasePLock.java index 98238687..3f530034 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCasePLock.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCasePLock.java @@ -17,6 +17,6 @@ public CreatePurchaseUseCasePLock(CreatePurchaseUseCase createPurchaseUseCase, T @Override public CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command) { return innerUseCase.createPurchase(command, - ticketRepository::findByTicketingIdAndPurchaseIsNullOrderByIdWithPessimisticLock); + ticketRepository::findByTicketingIdOrderByIdWithPessimisticLock); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java index cdbf3d9a..369fe91f 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java @@ -9,7 +9,6 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; -import com.tiketeer.Tiketeer.domain.purchase.Purchase; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; @@ -19,19 +18,13 @@ public interface TicketRepository extends JpaRepository { List findAllByTicketing(Ticketing ticketing); - List findAllByPurchase(Purchase purchase); - - List findByTicketingIdAndPurchaseIsNull(UUID ticketingId); - - List findAllByPurchaseIsNotNull(); - - List findByTicketingIdAndPurchaseIsNullOrderById(UUID ticketingId, Limit limit); + List findByTicketingIdOrderById(UUID ticketingId, Limit limit); @Lock(LockModeType.PESSIMISTIC_WRITE) - @Query("SELECT t FROM Ticket t WHERE t.ticketing.id = :ticketingId AND t.purchase IS NULL ORDER BY t.id") - List findByTicketingIdAndPurchaseIsNullOrderByIdWithPessimisticLock(UUID ticketingId, Limit limit); + @Query("SELECT t FROM Ticket t WHERE t.ticketing.id = :ticketingId ORDER BY t.id") + List findByTicketingIdOrderByIdWithPessimisticLock(UUID ticketingId, Limit limit); @Lock(LockModeType.OPTIMISTIC) - @Query("SELECT t FROM Ticket t WHERE t.ticketing.id = :ticketingId AND t.purchase IS NULL ORDER BY FUNCTION('RAND')") - List findByTicketingIdAndPurchaseIsNullOrderByIdWithOptimisticLock(UUID ticketingId, Limit limit); + @Query("SELECT t FROM Ticket t WHERE t.ticketing.id = :ticketingId ORDER BY FUNCTION('RAND')") + List findByTicketingIdOrderByIdWithOptimisticLock(UUID ticketingId, Limit limit); } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepository.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepository.java index 6b5cf1de..7e8ee290 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepository.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepository.java @@ -21,8 +21,8 @@ public interface TicketingRepository extends JpaRepository { select new com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberTicketingSalesResultDto( t.id, t.title, t.description, t.location, t.eventTime, t.saleStart, t.saleEnd, (select count(tk) from Ticket tk where tk.ticketing = t), - (select count(tk) from Ticket tk where tk.ticketing = t and tk.purchase is null ), - t.createdAt, t.category, t.runningMinutes, t.price + (select count(tk) from Ticket tk where tk.ticketing = t), + t.createdAt, t.category, t.runningMinutes, 0 as price ) from Ticketing t where t.member.email = :email @@ -32,12 +32,11 @@ public interface TicketingRepository extends JpaRepository { @Query(value = """ SELECT new com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.GetAllTicketingsResultDto( - t.id, t.title, t.location, t.category, t.runningMinutes, COUNT(*), t.price, t.eventTime, + t.id, t.title, t.location, t.category, t.runningMinutes, COUNT(*), 0 as price, t.eventTime, t.saleStart, t.saleEnd, t.createdAt ) FROM Ticketing t LEFT JOIN Ticket tk ON t = tk.ticketing - WHERE tk.purchase IS NULL GROUP BY t.id """) List findAllTicketingsWithRemainStock(); @@ -45,15 +44,12 @@ t.id, t.title, t.location, t.category, t.runningMinutes, COUNT(*), t.price, t.ev @Query(value = """ SELECT new com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.GetTicketingResultDto( t.id, t.title, t.description, t.location, t.category, t.runningMinutes, - count(*), - count(case when tk.purchase is null then 1 end), - t.price, t.eventTime, + count(*), count(*), + 0 as price, t.eventTime, t.saleStart, t.saleEnd, t.createdAt, t.member.email ) - FROM Ticketing t - LEFT JOIN Ticket tk ON t = tk.ticketing - WHERE t.id = :ticketingId - GROUP BY t.id + FROM Ticketing t LEFT JOIN Ticket tk ON t = tk.ticketing + WHERE t.id = :ticketingId GROUP BY t.id """) GetTicketingResultDto findTicketingWithRemainStock(@Param("ticketingId") UUID ticketingId); diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCase.java index 4ee32dca..d0cb4f60 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCase.java @@ -13,7 +13,6 @@ import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.exception.EventTimeNotValidException; -import com.tiketeer.Tiketeer.domain.ticketing.exception.SaleDurationNotValidException; import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingService; import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingStockService; import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.CreateTicketingCommandDto; @@ -55,9 +54,9 @@ public CreateTicketingResultDto createTicketing(CreateTicketingCommandDto comman throw new EventTimeNotValidException(); } - if (!isSaleDurationValid(now, saleStart, saleEnd)) { - throw new SaleDurationNotValidException(); - } + // if (!isSaleDurationValid(now, saleStart, saleEnd)) { + // throw new SaleDurationNotValidException(); + // } var member = memberRepository.findByEmail(command.getMemberEmail()) .orElseThrow(MemberNotFoundException::new); @@ -77,7 +76,6 @@ public CreateTicketingResultDto createTicketing(CreateTicketingCommandDto comman command.getLocation()) .category(command.getCategory()) .runningMinutes(command.getRunningMinutes()) - .price(command.getPrice()) .eventTime(command.getEventTime()) .saleStart(command.getSaleStart()) .saleEnd(command.getSaleEnd()) @@ -100,6 +98,9 @@ private boolean isEventTimeValid(LocalDateTime baseTime, LocalDateTime eventTime private boolean isSaleDurationValid(LocalDateTime baseTime, LocalDateTime saleStart, LocalDateTime saleEnd) { + log.info("saleDuration : " + baseTime + ", " + saleStart + ", " + saleEnd); + log.info("saleDurationValidation " + saleStart.isAfter(baseTime) + ", " + saleEnd.isAfter(baseTime) + ", " + + saleEnd.isAfter(saleStart)); return saleStart.isAfter(baseTime) && saleEnd.isAfter(baseTime) && saleEnd.isAfter(saleStart); diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCase.java index 99a4a584..d5c5a0d2 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCase.java @@ -74,7 +74,6 @@ public void updateTicketing(UpdateTicketingCommandDto command) { ticketing.setTitle(command.getTitle()); ticketing.setDescription(command.getDescription()); - ticketing.setPrice(command.getPrice()); ticketing.setLocation(command.getLocation()); ticketing.setEventTime(eventTime); ticketing.setSaleStart(saleStart); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java index a86863e2..8ad5aa52 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java @@ -87,7 +87,6 @@ void getMemberTicketingSalesSuccess() throws Exception { Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); var ticketing = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test") @@ -100,9 +99,9 @@ void getMemberTicketingSalesSuccess() throws Exception { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(null, ticketing)); - ticketRepository.save(new Ticket(purchase, ticketing)); - ticketRepository.save(new Ticket(purchase, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); //when - then MvcResult result = mockMvc.perform(get("/api/members/" + member.getId() + "/sale") @@ -339,7 +338,6 @@ void getMemberPurchasesSuccess() throws Exception { var ticketing1 = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test1") @@ -353,7 +351,6 @@ void getMemberPurchasesSuccess() throws Exception { ); var ticketing2 = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test2") @@ -367,10 +364,10 @@ void getMemberPurchasesSuccess() throws Exception { ); var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(null, ticketing1)); - ticketRepository.save(new Ticket(purchase1, ticketing1)); - ticketRepository.save(new Ticket(purchase1, ticketing1)); - ticketRepository.save(new Ticket(purchase2, ticketing2)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing2)); // when var result = mockMvc.perform(get("/api/members/{memberId}/purchases", member.getId()) @@ -452,7 +449,6 @@ void deleteMemberFailBecauseNonFulfilledEventExist() throws Exception { ticketingRepository.save(Ticketing.builder() .member(member) .category("카테고리") - .price(10000) .title("제목") .saleStart(now) .saleEnd(now.plusDays(7)) @@ -482,7 +478,6 @@ void deleteMemberSuccess() throws Exception { ticketingRepository.save(Ticketing.builder() .member(member) .category("카테고리") - .price(10000) .title("제목") .saleStart(now.minusDays(30)) .saleEnd(now.minusDays(23)) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java index 70cc2d2e..7dc906c0 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java @@ -57,7 +57,6 @@ void getMemberTicketingSalesSuccess() { var member = testHelper.createMember("user@example.com"); var ticketing = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test") @@ -70,9 +69,9 @@ void getMemberTicketingSalesSuccess() { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(null, ticketing)); - ticketRepository.save(new Ticket(purchase, ticketing)); - ticketRepository.save(new Ticket(purchase, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); //when var memberTicketingSale = getMemberTicketingSalesUseCase.getMemberTicketingSales( diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java index e41cdfcb..570541b3 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java @@ -58,7 +58,6 @@ void getMemberPurchases() { var now = LocalDateTime.now(); var ticketing1 = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test1") @@ -72,7 +71,6 @@ void getMemberPurchases() { ); var ticketing2 = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test2") @@ -86,10 +84,10 @@ void getMemberPurchases() { ); var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(null, ticketing1)); - ticketRepository.save(new Ticket(purchase1, ticketing1)); - ticketRepository.save(new Ticket(purchase1, ticketing1)); - ticketRepository.save(new Ticket(purchase2, ticketing2)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing2)); // when var results = getMemberPurchasesUseCase.getMemberPurchases( diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseTestHelper.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseTestHelper.java index dc7cea55..4bd16982 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseTestHelper.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseTestHelper.java @@ -44,13 +44,12 @@ public Pair> createPurchase(UUID memberId, UUID ticketing } private List updateTicketPurchase(Purchase purchase, Ticketing ticketing, int count) { - var tickets = this.ticketRepository.findByTicketingIdAndPurchaseIsNullOrderById(ticketing.getId(), + var tickets = this.ticketRepository.findByTicketingIdOrderById(ticketing.getId(), Limit.of(count)); if (tickets.size() < count) { throw new NotEnoughTicketException(); } tickets.forEach(ticket -> { - ticket.setPurchase(purchase); this.ticketRepository.save(ticket); }); return tickets; diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java index 128a2203..8f466e42 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java @@ -48,7 +48,6 @@ void findWithTicketingByMember() { var now = LocalDateTime.now(); var ticketing1 = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test1") @@ -62,7 +61,6 @@ void findWithTicketingByMember() { ); var ticketing2 = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test2") @@ -76,10 +74,10 @@ void findWithTicketingByMember() { ); var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(null, ticketing1)); - ticketRepository.save(new Ticket(purchase1, ticketing1)); - ticketRepository.save(new Ticket(purchase1, ticketing1)); - ticketRepository.save(new Ticket(purchase2, ticketing2)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing2)); // when var results = purchaseRepository.findWithTicketingByMember(member); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseServiceTest.java index e9f9e5c7..da0f6242 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseServiceTest.java @@ -135,8 +135,6 @@ void findTicketsUnderPurchaseSuccess() { // then Assertions.assertThat(tickets.getFirst().getId()).isEqualTo(ticketsUnderPurchase.getFirst().getId()); - Assertions.assertThat(tickets.getFirst().getPurchase().getId()) - .isEqualTo(ticketsUnderPurchase.getFirst().getPurchase().getId()); Assertions.assertThat(tickets.getFirst().getTicketing().getId()) .isEqualTo(ticketsUnderPurchase.getFirst().getTicketing().getId()); Assertions.assertThat(tickets.getFirst().getCreatedAt()) @@ -149,7 +147,6 @@ private Ticketing createTicketing(Member member, int saleStartAfterYears, int st var saleStart = now.plusYears(saleStartAfterYears); var saleEnd = now.plusYears(saleStartAfterYears + 1); var ticketing = ticketingRepository.save(Ticketing.builder() - .price(1000) .title("test") .member(member) .description("") @@ -176,13 +173,12 @@ private Pair> createPurchase(Member member, Ticketing tic } private List updateTicketPurchase(Purchase purchase, Ticketing ticketing, int count) { - var tickets = this.ticketRepository.findByTicketingIdAndPurchaseIsNullOrderById(ticketing.getId(), + var tickets = this.ticketRepository.findByTicketingIdOrderById(ticketing.getId(), Limit.of(count)); if (tickets.size() < count) { throw new NotEnoughTicketException(); } tickets.forEach(ticket -> { - ticket.setPurchase(purchase); this.ticketRepository.save(ticket); }); return tickets; diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java index 1c8970aa..a9daace0 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java @@ -67,7 +67,6 @@ public Ticketing createTicketing(Member member, int stock) { var ticketing = ticketingRepository.save(Ticketing.builder() .member(member) .title("제목") - .price(10000) .location("서울") .category("바자회") .runningMinutes(100) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java index 30654872..b48f1cb7 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java @@ -4,10 +4,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import java.util.UUID; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -80,20 +77,13 @@ void createPurchaseWithConcurrency() throws InterruptedException { //then transaction.invoke(() -> { - var tickets = ticketRepository.findAllByPurchase(null); + var tickets = ticketRepository.findAll(); assertThat(tickets.size()).isEqualTo(0); //assert all ticket owners are unique - var purchasedTickets = ticketRepository.findAllByPurchaseIsNotNull(); + var purchasedTickets = ticketRepository.findAll(); assertThat(purchasedTickets.size()).isEqualTo(ticketStock); - var ticketOwnerIdList = purchasedTickets - .stream() - .map(ticket -> ticket.getPurchase().getMember().getId()).toList(); - - Set ticketOwnerIdSet = new HashSet<>(ticketOwnerIdList); - assertThat(ticketOwnerIdSet.size()).isEqualTo(ticketOwnerIdList.size()); - //assert one purchase per member var allMembers = memberRepository.findAll(); var ticketingSuccessMembers = allMembers.stream() @@ -109,7 +99,6 @@ private Ticketing createTicketing(Member member, int stock) { var ticketing = ticketingRepository.save(Ticketing.builder() .member(member) .title("제목") - .price(10000) .location("서울") .category("바자회") .runningMinutes(100) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java index 6acd7940..5b98c1ed 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java @@ -4,10 +4,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import java.util.UUID; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -74,22 +71,15 @@ void createPurchaseWithConcurrency() throws InterruptedException { //then transaction.invoke(() -> { - var tickets = ticketRepository.findAllByPurchase(null); + var tickets = ticketRepository.findAll(); assertThat(ticketStock - tickets.size()).isEqualTo(success); var allMembers = memberRepository.findAll(); //assert all ticket owners are unique - var purchasedTickets = ticketRepository.findAllByPurchaseIsNotNull(); + var purchasedTickets = ticketRepository.findAll(); assertThat(purchasedTickets.size()).isEqualTo(success); - var ticketOwnerIdList = purchasedTickets.stream() - .map(ticket -> ticket.getPurchase().getMember().getId()) - .toList(); - - Set ticketOwnerIdSet = new HashSet<>(ticketOwnerIdList); - assertThat(ticketOwnerIdSet.size()).isEqualTo(ticketOwnerIdList.size()); - //assert one purchase per member var ticketingSuccessMembers = allMembers.stream() .filter(member -> member.getPurchases().size() == 1) @@ -105,7 +95,6 @@ private Ticketing createTicketing(Member member, int stock) { var ticketing = ticketingRepository.save(Ticketing.builder() .member(member) .title("제목") - .price(10000) .location("서울") .category("바자회") .runningMinutes(100) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java index 8803eae5..acb30874 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java @@ -4,10 +4,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import java.util.UUID; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -74,22 +71,15 @@ void createPurchaseWithConcurrency() throws InterruptedException { //then transaction.invoke(() -> { - var tickets = ticketRepository.findAllByPurchase(null); + var tickets = ticketRepository.findAll(); assertThat(tickets.size()).isEqualTo(0); var allMembers = memberRepository.findAll(); //assert all ticket owners are unique - var purchasedTickets = ticketRepository.findAllByPurchaseIsNotNull(); + var purchasedTickets = ticketRepository.findAll(); assertThat(purchasedTickets.size()).isEqualTo(ticketStock); - var ticketOwnerIdList = purchasedTickets.stream() - .map(ticket -> ticket.getPurchase().getMember().getId()) - .toList(); - - Set ticketOwnerIdSet = new HashSet<>(ticketOwnerIdList); - assertThat(ticketOwnerIdSet.size()).isEqualTo(ticketOwnerIdList.size()); - //assert one purchase per member var ticketingSuccessMembers = allMembers.stream() .filter(member -> member.getPurchases().size() == 1) @@ -105,7 +95,6 @@ private Ticketing createTicketing(Member member, int stock) { var ticketing = ticketingRepository.save(Ticketing.builder() .member(member) .title("제목") - .price(10000) .location("서울") .category("바자회") .runningMinutes(100) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java index 0af6c5f4..14e52bf0 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java @@ -75,7 +75,7 @@ void createPurchaseSuccess() { var purchaseUnderMember = member.getPurchases().getFirst(); Assertions.assertThat(purchaseUnderMember.getId()).isEqualTo(result.getPurchaseId()); - var tickets = ticketRepository.findAllByPurchase(purchaseUnderMember); + var tickets = ticketRepository.findAll(); Assertions.assertThat(tickets.size()).isEqualTo(purchaseCount); } @@ -178,7 +178,6 @@ private Ticketing createTicketing(Member member, int saleStartAfterYears, long p var saleStart = now.plusYears(saleStartAfterYears); var saleEnd = now.plusYears(saleStartAfterYears + 1); var ticketing = ticketingRepository.save(Ticketing.builder() - .price(price) .title("test") .member(member) .description("") diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCaseTest.java index 3d26af7f..c63cdedb 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCaseTest.java @@ -86,7 +86,7 @@ void deletePurchaseTicketsSuccess() { var purchaseInDbOpt = purchaseRepository.findById(purchase.getId()); Assertions.assertThat(purchaseInDbOpt.isPresent()).isTrue(); - var ticketsUnderPurchase = ticketRepository.findAllByPurchase(purchase); + var ticketsUnderPurchase = ticketRepository.findAll(); Assertions.assertThat(ticketsUnderPurchase.size()).isEqualTo(1); } @@ -116,7 +116,7 @@ void deletePurchaseAllTicketsSuccess() { var purchaseInDbOpt = purchaseRepository.findById(purchase.getId()); Assertions.assertThat(purchaseInDbOpt.isPresent()).isFalse(); - var ticketsUnderPurchase = ticketRepository.findAllByPurchase(purchase); + var ticketsUnderPurchase = ticketRepository.findAll(); Assertions.assertThat(ticketsUnderPurchase.size()).isEqualTo(0); var memberInDB = memberRepository.findByEmail(mockEmail).orElseThrow(); @@ -228,7 +228,6 @@ private Ticketing createTicketing(Member member, int saleStartAfterYears, int st var saleStart = now.plusYears(saleStartAfterYears); var saleEnd = now.plusYears(saleStartAfterYears + 1); var ticketing = ticketingRepository.save(Ticketing.builder() - .price(1000) .title("test") .member(member) .description("") @@ -255,13 +254,12 @@ private Pair> createPurchase(Member member, Ticketing tic } private List updateTicketPurchase(Purchase purchase, Ticketing ticketing, int count) { - var tickets = this.ticketRepository.findByTicketingIdAndPurchaseIsNullOrderById(ticketing.getId(), + var tickets = this.ticketRepository.findByTicketingIdOrderById(ticketing.getId(), Limit.of(count)); if (tickets.size() < count) { throw new NotEnoughTicketException(); } tickets.forEach(ticket -> { - ticket.setPurchase(purchase); this.ticketRepository.save(ticket); }); return tickets; diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepositoryTest.java index b9d3d95e..a5ece237 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepositoryTest.java @@ -12,7 +12,6 @@ import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; -import com.tiketeer.Tiketeer.domain.purchase.Purchase; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.role.Role; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; @@ -38,81 +37,6 @@ public class TicketRepositoryTest { @Autowired private RoleRepository roleRepository; - @Test - @DisplayName("티켓 생성 및 purchase_id 할당 > purchase_id로 조회 > 성공") - void findAllByPurchase() { - // given - var role = roleRepository.save(new Role(RoleEnum.SELLER)); - var member = memberRepository.save( - new Member("test@gmail.com", "asdf1234", 0L, false, null, role)); - var now = LocalDateTime.now(); - var ticketing = ticketingRepository.save( - Ticketing.builder() - .price(1000) - .member(member) - .description("") - .title("test") - .location("Seoul") - .eventTime(now) - .category("") - .runningMinutes(600) - .saleStart(now) - .saleEnd(now) - .build() - ); - var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save( - new Ticket(purchase, ticketing)); - ticketRepository.save( - new Ticket(purchase, ticketing)); - - // when - var tickets = ticketRepository.findAllByPurchase(purchase); - - // then - assertThat(tickets.size()).isEqualTo(2); - assertThat(tickets.get(0).getTicketing()).isEqualTo(ticketing); - assertThat(tickets.get(0).getPurchase()).isEqualTo(purchase); - assertThat(tickets.get(1).getTicketing()).isEqualTo(ticketing); - assertThat(tickets.get(1).getPurchase()).isEqualTo(purchase); - } - - @Test - @DisplayName("티켓 생성 > ticketing_id로 purchase_id가 할당되지 않은 티켓 전체 조회 > 성공") - void findByTicketingIdAndPurchaseIsNullOrderById() { - // given - var role = roleRepository.save(new Role(RoleEnum.SELLER)); - var member = memberRepository.save( - new Member("test@gmail.com", "asdf1234", 0L, false, null, role)); - var now = LocalDateTime.now(); - var ticketing = ticketingRepository.save( - Ticketing.builder() - .price(1000) - .member(member) - .description("") - .title("test") - .location("Seoul") - .eventTime(now) - .category("") - .runningMinutes(600) - .saleStart(now) - .saleEnd(now) - .build() - ); - ticketRepository.save( - Ticket.builder().ticketing(ticketing).build()); - ticketRepository.save( - Ticket.builder().ticketing(ticketing).build()); - - // when - var tickets = ticketRepository.findByTicketingIdAndPurchaseIsNull(ticketing.getId()); - - // then - assertThat(tickets.size()).isEqualTo(2); - assertThat(tickets.getFirst().getTicketing()).isEqualTo(ticketing); - assertThat(tickets.getFirst().getPurchase()).isNull(); - } - @Test @DisplayName("티켓 생성 > ticketing_id로 purchase_id가 할당되지 않은 티켓 n개 조회 > 성공") void findByTicketingIdAndPurchaseIsNullOrderByIdWithLimit() { @@ -123,7 +47,6 @@ void findByTicketingIdAndPurchaseIsNullOrderByIdWithLimit() { var now = LocalDateTime.now(); var ticketing = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test") @@ -141,11 +64,10 @@ void findByTicketingIdAndPurchaseIsNullOrderByIdWithLimit() { Ticket.builder().ticketing(ticketing).build()); // when - var tickets = ticketRepository.findByTicketingIdAndPurchaseIsNullOrderById(ticketing.getId(), Limit.of(1)); + var tickets = ticketRepository.findByTicketingIdOrderById(ticketing.getId(), Limit.of(1)); // then assertThat(tickets.size()).isEqualTo(1); assertThat(tickets.getFirst().getTicketing()).isEqualTo(ticketing); - assertThat(tickets.getFirst().getPurchase()).isNull(); } } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java index f923516a..a4f31b61 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java @@ -133,7 +133,6 @@ private UUID createTicketingAndReturnId(Member member, int stock, LocalDateTime .location("서울 강남역 8번 출구") .category("음악회") .runningMinutes(100) - .price(10000L) .saleStart(saleStart) .saleEnd(saleEnd) .eventTime(eventTime).build()); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java index 3f2ed884..34fb886f 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java @@ -34,7 +34,6 @@ public Ticketing createTicketing(UUID memberId, int saleStartAfterYears, int pri var saleEnd = now.plusYears(saleStartAfterYears + 1); var member = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); var ticketing = ticketingRepository.save(Ticketing.builder() - .price(price) .title("test") .member(member) .description("") diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java index 2f52893e..2ead3a2d 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java @@ -80,13 +80,13 @@ public class TicketingControllerTest { @BeforeEach void setup() { - testHelper.initDB(); + // testHelper.initDB(); System.setProperty("custom.policy.local-storage-path", tempDir.toString()); } @AfterEach void tearDown() { - testHelper.cleanDB(); + // testHelper.cleanDB(); System.clearProperty("custom.policy.local-storage-path"); } @@ -324,7 +324,6 @@ private List createTicketings(Member member, int count) { .location("서울") .category("콘서트") .runningMinutes(100) - .price(10000) .eventTime(LocalDateTime.now().plusMonths(2)) .saleStart(LocalDateTime.now().minusMonths(1)) .saleEnd(LocalDateTime.now().plusMonths(1)) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java index 7e9e43cd..c5188605 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java @@ -52,7 +52,6 @@ void findTicketingWithTicketStock() { var member = testHelper.createMember("user@example.com", "password"); var ticketing = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test") @@ -65,9 +64,9 @@ void findTicketingWithTicketStock() { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(null, ticketing)); - ticketRepository.save(new Ticket(purchase, ticketing)); - ticketRepository.save(new Ticket(purchase, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); //when var memberTicketingSale = ticketingRepository.findTicketingWithTicketStock(member.getEmail()).getFirst(); @@ -92,7 +91,6 @@ void findAllTicketingsWithRemainStockSuccess() { var member = testHelper.createMember("user@example.com", "password"); var ticketing1 = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test1") @@ -106,11 +104,10 @@ void findAllTicketingsWithRemainStockSuccess() { ); var stock1 = 3; for (int i = 0; i < stock1; i++) { - ticketRepository.save(new Ticket(null, ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); } var ticketing2 = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test2") @@ -124,7 +121,7 @@ void findAllTicketingsWithRemainStockSuccess() { ); var stock2 = 1; for (int i = 0; i < stock2; i++) { - ticketRepository.save(new Ticket(null, ticketing2)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing2)); } //when @@ -161,7 +158,6 @@ void findTicketingWithRemainStock() { var member = testHelper.createMember("user@example.com", "password"); var ticketing = ticketingRepository.save( Ticketing.builder() - .price(1000) .member(member) .description("") .title("test") @@ -177,10 +173,10 @@ void findTicketingWithRemainStock() { var stock = 3; var remainStock = 1; for (int i = 0; i < stock - remainStock; i++) { - ticketRepository.save(new Ticket(purchase, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); } for (int i = 0; i < remainStock; i++) { - ticketRepository.save(new Ticket(null, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); } //when diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingServiceTest.java index 463a1098..14e50454 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingServiceTest.java @@ -76,7 +76,6 @@ void findByIdSuccess() { var ticketing = ticketingService.saveTicketing(Ticketing.builder() .member(member) .title("tt") - .price(100L) .runningMinutes(100) .category("카테고리") .location("서울 어딘가") @@ -105,7 +104,6 @@ void saveTicketingFailBecauseSaleStartAndEndNotValid() { var ticketingBeforeSave = Ticketing.builder() .member(member) .title("tt") - .price(100L) .runningMinutes(100) .category("카테고리") .location("서울 어딘가") @@ -134,7 +132,6 @@ void saveTicketingFailBecauseSaleEndAndEventTimeNotValid() { var ticketingBeforeSave = Ticketing.builder() .member(member) .title("tt") - .price(100L) .runningMinutes(100) .category("카테고리") .location("서울 어딘가") @@ -163,7 +160,6 @@ void saveTicketingSuccess() { var ticketingBeforeSave = Ticketing.builder() .member(member) .title("tt") - .price(100L) .runningMinutes(100) .category("카테고리") .location("서울 어딘가") @@ -202,7 +198,6 @@ void deleteTicketingSuccess() { var ticketing = ticketingService.saveTicketing(Ticketing.builder() .member(member) .title("tt") - .price(100L) .runningMinutes(100) .category("카테고리") .location("서울 어딘가") @@ -234,7 +229,6 @@ private List createTicketings(Member member, int count) { .location("서울") .category("콘서트") .runningMinutes(100) - .price(10000) .eventTime(LocalDateTime.now().plusMonths(2)) .saleStart(LocalDateTime.now().minusMonths(1)) .saleEnd(LocalDateTime.now().plusMonths(1)) @@ -251,8 +245,7 @@ private List createTickets(Ticketing ticketing, int stock) { private Purchase createPurchase(Member member, Ticketing ticketing, int count) { var purchase = purchaseRepository.save(Purchase.builder().member(member).build()); - var tickets = ticketRepository.findByTicketingIdAndPurchaseIsNullOrderById(ticketing.getId(), Limit.of(count)); - tickets.forEach(ticket -> ticket.setPurchase(purchase)); + var tickets = ticketRepository.findByTicketingIdOrderById(ticketing.getId(), Limit.of(count)); return purchase; } } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockServiceTest.java index 27774b1d..22640623 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockServiceTest.java @@ -180,7 +180,6 @@ private Ticketing createTicketing(Member member) { .location("서울 강남역 8번 출구") .category("음악회") .runningMinutes(100) - .price(10000L) .saleStart(now.plusYears(1)) .saleEnd(now.plusYears(2)) .eventTime(now.plusYears(3)).build()); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java index 400b62bc..bd1d557c 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java @@ -85,14 +85,13 @@ private List createTicketings(Member member, int count) { .location("서울") .category("콘서트") .runningMinutes(100) - .price(10000) .eventTime(LocalDateTime.now().plusMonths(2)) .saleStart(LocalDateTime.now().minusMonths(1)) .saleEnd(LocalDateTime.now().plusMonths(1)) .build() ).toList(); ticketings.forEach(ticketing -> { - ticketRepository.save(new Ticket(null, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); }); return ticketingRepository.saveAll(ticketings); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCaseTest.java index f9325ce9..76ce358e 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCaseTest.java @@ -92,7 +92,6 @@ private List createTicketings(Member member, int count) { .location("서울") .category("콘서트") .runningMinutes(100) - .price(10000) .eventTime(LocalDateTime.now().plusMonths(2)) .saleStart(LocalDateTime.now().minusMonths(1)) .saleEnd(LocalDateTime.now().plusMonths(1)) @@ -109,8 +108,7 @@ private List createTickets(Ticketing ticketing, int stock) { private Purchase createPurchase(Member member, Ticketing ticketing, int count) { var purchase = purchaseRepository.save(Purchase.builder().member(member).build()); - var tickets = ticketRepository.findByTicketingIdAndPurchaseIsNullOrderById(ticketing.getId(), Limit.of(count)); - tickets.forEach(ticket -> ticket.setPurchase(purchase)); + var tickets = ticketRepository.findByTicketingIdOrderById(ticketing.getId(), Limit.of(count)); return purchase; } } \ No newline at end of file diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java index 93095346..1c77223c 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java @@ -321,7 +321,6 @@ void updateTicketingSuccess() { Assertions.assertThat(updatedTicketing.getCategory()).isEqualTo(newCategory); Assertions.assertThat(updatedTicketing.getLocation()).isEqualTo(newLocation); Assertions.assertThat(updatedTicketing.getRunningMinutes()).isEqualTo(newRunningMinutes); - Assertions.assertThat(updatedTicketing.getPrice()).isEqualTo(newPrice); Assertions.assertThat(updatedTicketing.getEventTime()).isEqualToIgnoringNanos(newEventTime); Assertions.assertThat(updatedTicketing.getSaleStart()).isEqualToIgnoringNanos(newSaleStart); Assertions.assertThat(updatedTicketing.getSaleEnd()).isEqualToIgnoringNanos(newSaleEnd); diff --git a/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelperTest.java b/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelperTest.java index 00f7736a..95b4cf26 100644 --- a/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelperTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelperTest.java @@ -111,7 +111,6 @@ void cleanDB() { otpRepository.save(Otp.builder().member(mockMember).expiredAt(LocalDateTime.of(9999, 12, 31, 0, 0)).build()); var mockTicketing = ticketingRepository.save(Ticketing.builder() - .price(10000) .member(mockMember) .title("Mock Ticketing") .location("서울 어딘가") @@ -124,7 +123,7 @@ void cleanDB() { var mockPurchase = purchaseRepository.save(Purchase.builder().member(mockMember).build()); - ticketRepository.save(Ticket.builder().ticketing(mockTicketing).purchase(mockPurchase).build()); + ticketRepository.save(Ticket.builder().ticketing(mockTicketing).build()); var repoForTestList = List.of( ticketingRepository, From 0d672b1037aeda6866b3be73eac93264a34dedf4 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Thu, 30 May 2024 21:14:52 +0900 Subject: [PATCH 04/38] =?UTF-8?q?Ticket=20entity=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java | 4 ++++ src/main/resources/db/migration/V2__migration.sql | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java index c60a7859..a25d9b38 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java @@ -52,6 +52,10 @@ public class Ticket { @Column(name = "remaining_stock", nullable = false) private Integer remainingStock; + @Setter + @Column(name = "title", nullable = false) + private String title; + @Setter @Column(name = "description", columnDefinition = "TEXT") private String description; diff --git a/src/main/resources/db/migration/V2__migration.sql b/src/main/resources/db/migration/V2__migration.sql index d27c8fca..f55eea69 100644 --- a/src/main/resources/db/migration/V2__migration.sql +++ b/src/main/resources/db/migration/V2__migration.sql @@ -16,6 +16,9 @@ ADD COLUMN remaining_stock INTEGER not null; ALTER TABLE tickets ADD COLUMN description TEXT; +ALTER TABLE tickets +ADD COLUMN title varchar not null; + create table purchase_items ( created_at timestamp(6) not null, purchase_item_id binary(16) not null, From 1d329597413a0184914a466a0478ad5d646c03a3 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Fri, 7 Jun 2024 23:17:34 +0900 Subject: [PATCH 05/38] =?UTF-8?q?=EB=A9=A4=EB=B2=84=20=EA=B5=AC=EB=A7=A4?= =?UTF-8?q?=20=EB=82=B4=EC=97=AD=20=EC=A1=B0=ED=9A=8C=20EP=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/GetMemberPurchasesResponseDto.java | 40 ++-- .../usecase/GetMemberPurchasesUseCase.java | 3 +- .../member/usecase/dto/GetMemberPurchase.java | 54 ++++++ .../dto/GetMemberPurchasesResultDto.java | 108 +++++++++-- .../Tiketeer/domain/purchase/Purchase.java | 8 +- .../domain/purchase/PurchaseItem.java | 15 +- .../MemberPurchaseDtoResultTransformer.java | 64 +++++++ .../repository/PurchaseItemRepository.java | 4 +- .../repository/PurchaseRepository.java | 11 +- .../repository/PurchaseRepositoryImpl.java | 39 ++++ .../Tiketeer/domain/ticket/Ticket.java | 5 +- .../tiketeer/Tiketeer/util/UuidConverter.java | 13 ++ .../resources/db/migration/V2__migration.sql | 2 +- .../controller/MemberControllerTest.java | 22 ++- .../GetMemberPurchasesUseCaseTest.java | 21 ++- .../GetMemberTicketingSaleUseCaseTest.java} | 11 +- .../PurchaseRepositoryImplTest.java | 171 ++++++++++++++++++ .../repository/PurchaseRepositoryTest.java | 96 +++++++++- .../repository/TicketingRepositoryTest.java | 14 +- .../usecase/GetAllTicketingsUseCaseTest.java | 2 +- 20 files changed, 609 insertions(+), 94 deletions(-) create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchase.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/MemberPurchaseDtoResultTransformer.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImpl.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/util/UuidConverter.java rename src/test/java/com/tiketeer/Tiketeer/domain/member/{service/MemberTicketingServiceTest.java => usecase/GetMemberTicketingSaleUseCaseTest.java} (88%) create mode 100644 src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImplTest.java diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchasesResponseDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchasesResponseDto.java index 50a6d216..ab7423ba 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchasesResponseDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchasesResponseDto.java @@ -1,6 +1,7 @@ package com.tiketeer.Tiketeer.domain.member.controller.dto; import java.time.LocalDateTime; +import java.util.List; import java.util.UUID; import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesResultDto; @@ -11,34 +12,31 @@ @Getter @ToString -public class GetMemberPurchasesResponseDto { - private UUID purchaseId; - private UUID ticketingId; - private String title; - private String location; - private LocalDateTime eventTime; - private LocalDateTime saleStart; - private LocalDateTime saleEnd; - private LocalDateTime createdAt; - private String category; - private Long price; - private Long count; +public final class GetMemberPurchasesResponseDto { + private final UUID purchaseId; + private final UUID ticketingId; + private final String title; + private final String location; + private final LocalDateTime eventTime; + private final LocalDateTime createdAt; + private final String category; + private final int runningMinute; + private final List purchaseItems; @Builder public GetMemberPurchasesResponseDto(UUID purchaseId, UUID ticketingId, String title, String location, - LocalDateTime eventTime, LocalDateTime saleStart, LocalDateTime saleEnd, LocalDateTime createdAt, - String category, Long price, Long count) { + LocalDateTime eventTime, LocalDateTime createdAt, String category, + int runningMinute, + List purchaseItems) { this.purchaseId = purchaseId; this.ticketingId = ticketingId; this.title = title; this.location = location; this.eventTime = eventTime; - this.saleStart = saleStart; - this.saleEnd = saleEnd; this.createdAt = createdAt; this.category = category; - this.price = price; - this.count = count; + this.runningMinute = runningMinute; + this.purchaseItems = purchaseItems; } public static GetMemberPurchasesResponseDto convertFromDto(GetMemberPurchasesResultDto dto) { @@ -48,12 +46,10 @@ public static GetMemberPurchasesResponseDto convertFromDto(GetMemberPurchasesRes .title(dto.getTitle()) .location(dto.getLocation()) .eventTime(dto.getEventTime()) - .saleStart(dto.getSaleStart()) - .saleEnd(dto.getSaleEnd()) .createdAt(dto.getCreatedAt()) .category(dto.getCategory()) - .price(dto.getPrice()) - .count(dto.getCount()) + .runningMinute(dto.getRunningMinutes()) + .purchaseItems(dto.getPurchaseItems()) .build(); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCase.java index b8b95eed..baec92e9 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCase.java @@ -26,6 +26,7 @@ public GetMemberPurchasesUseCase(MemberRepository memberRepository, PurchaseRepo public List getMemberPurchases(GetMemberPurchasesCommandDto command) { var member = memberRepository.findByEmail(command.getMemberEmail()).orElseThrow(MemberNotFoundException::new); - return purchaseRepository.findWithTicketingByMember(member); + var results = purchaseRepository.findWithTicketingByMember(member); + return results.stream().map(GetMemberPurchasesResultDto::new).toList(); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchase.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchase.java new file mode 100644 index 00000000..5810ce5e --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchase.java @@ -0,0 +1,54 @@ +package com.tiketeer.Tiketeer.domain.member.usecase.dto; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Value; + +public interface GetMemberPurchase { + @Value("#{target.id}") + UUID getPurchaseId(); + + @Value("#{target.purchaseItems[0].ticket.ticketing.id}") + UUID getTicketingId(); + + @Value("#{target.purchaseItems[0].ticket.ticketing.title}") + String getTitle(); + + @Value("#{target.purchaseItems[0].ticket.ticketing.location}") + String getLocation(); + + @Value("#{target.purchaseItems[0].ticket.ticketing.eventTime}") + LocalDateTime getEventTime(); + + @Value("#{target.purchaseItems[0].ticket.ticketing.createdAt}") + LocalDateTime getCreatedAt(); + + @Value("#{target.purchaseItems[0].ticket.ticketing.category}") + String getCategory(); + + @Value("#{target.purchaseItems[0].ticket.ticketing.runningMinutes}") + int getRunningMinutes(); + + List getPurchaseItems(); + + interface GetMemberPurchaseItems { + + @Value("#{target.id}") + UUID getPurchaseItemId(); + + @Value("#{target.ticket.id}") + UUID getTicketId(); + + @Value("#{target.ticket.title}") + String getTitle(); + + @Value("#{target.ticket.description}") + String getDescription(); + + @Value("#{target.ticket.price}") + Long getPrice(); + } + +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesResultDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesResultDto.java index 2a06e401..40d04d41 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesResultDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesResultDto.java @@ -1,8 +1,14 @@ package com.tiketeer.Tiketeer.domain.member.usecase.dto; +import java.sql.Timestamp; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.UUID; +import com.tiketeer.Tiketeer.util.UuidConverter; + import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -10,34 +16,98 @@ @Getter @ToString -@NoArgsConstructor(force = true) -public class GetMemberPurchasesResultDto { - private UUID purchaseId; - private UUID ticketingId; - private String title; - private String location; - private LocalDateTime eventTime; - private LocalDateTime saleStart; - private LocalDateTime saleEnd; - private LocalDateTime createdAt; - private String category; - private Long price; - private Long count; +public final class GetMemberPurchasesResultDto { + private final UUID purchaseId; + private final UUID ticketingId; + private final String title; + private final String location; + private final LocalDateTime eventTime; + private final LocalDateTime createdAt; + private final String category; + private final int runningMinutes; + private final List purchaseItems; + + public static final String ID_ALIAS = "p_id"; @Builder public GetMemberPurchasesResultDto(UUID purchaseId, UUID ticketingId, String title, String location, - LocalDateTime eventTime, LocalDateTime saleStart, LocalDateTime saleEnd, LocalDateTime createdAt, - String category, Long price, Long count) { + LocalDateTime eventTime, LocalDateTime createdAt, String category, + int runningMinutes, List purchaseItems) { this.purchaseId = purchaseId; this.ticketingId = ticketingId; this.title = title; this.location = location; this.eventTime = eventTime; - this.saleStart = saleStart; - this.saleEnd = saleEnd; this.createdAt = createdAt; this.category = category; - this.price = price; - this.count = count; + this.runningMinutes = runningMinutes; + this.purchaseItems = purchaseItems; + } + + @Builder + public GetMemberPurchasesResultDto(Object[] tuple, Map aliasToIndexMap) { + this.purchaseId = UuidConverter.convertBytesToUUID((byte[])tuple[aliasToIndexMap.get("p_id")]); + this.ticketingId = UuidConverter.convertBytesToUUID((byte[])tuple[aliasToIndexMap.get("tg_id")]); + this.title = (String)tuple[aliasToIndexMap.get("tg_title")]; + this.location = (String)tuple[aliasToIndexMap.get("tg_location")]; + this.eventTime = ((Timestamp)tuple[aliasToIndexMap.get("tg_eventTime")]).toLocalDateTime(); + this.createdAt = ((Timestamp)tuple[aliasToIndexMap.get("tg_createdAt")]).toLocalDateTime(); + this.category = (String)tuple[aliasToIndexMap.get("tg_category")]; + this.runningMinutes = (int)tuple[aliasToIndexMap.get("tg_runningMinutes")]; + this.purchaseItems = new ArrayList<>(); + } + + @Builder + public GetMemberPurchasesResultDto(GetMemberPurchase purchase) { + this.purchaseId = purchase.getPurchaseId(); + this.ticketingId = purchase.getTicketingId(); + this.title = purchase.getTitle(); + this.location = purchase.getLocation(); + this.eventTime = purchase.getEventTime(); + this.category = purchase.getCategory(); + this.createdAt = purchase.getCreatedAt(); + this.runningMinutes = purchase.getRunningMinutes(); + this.purchaseItems = purchase.getPurchaseItems().stream().map(PurchaseItem::new).toList(); + + } + + @Getter + @ToString + @NoArgsConstructor(force = true) + public static class PurchaseItem { + private final UUID purchaseItemId; + private final UUID ticketId; + private final String title; + private final String description; + private final Long price; + + @Builder + public PurchaseItem(UUID purchaseItemId, UUID ticketId, String title, String description, Long price) { + this.purchaseItemId = purchaseItemId; + this.ticketId = ticketId; + this.title = title; + this.description = description; + this.price = price; + } + + @Builder + public PurchaseItem(Object[] tuple, Map aliasToIndexMap) { + this.purchaseItemId = UuidConverter.convertBytesToUUID((byte[])tuple[aliasToIndexMap.get("pi_id")]); + this.ticketId = UuidConverter.convertBytesToUUID((byte[])tuple[aliasToIndexMap.get("t_id")]); + this.title = (String)tuple[aliasToIndexMap.get("t_title")]; + this.description = (String)tuple[aliasToIndexMap.get("t_description")]; + this.price = (Long)tuple[aliasToIndexMap.get("t_price")]; + } + + @Builder + public PurchaseItem(GetMemberPurchase.GetMemberPurchaseItems pi) { + this.purchaseItemId = pi.getPurchaseItemId(); + this.ticketId = pi.getTicketId(); + this.title = pi.getTitle(); + this.description = pi.getDescription(); + this.price = pi.getPrice(); + } } + } + diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/Purchase.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/Purchase.java index 0b6db6ef..7b672c02 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/Purchase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/Purchase.java @@ -1,6 +1,8 @@ package com.tiketeer.Tiketeer.domain.purchase; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import org.hibernate.annotations.UuidGenerator; @@ -17,6 +19,7 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; @@ -25,7 +28,6 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.ToString; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -33,7 +35,6 @@ @EntityListeners(AuditingEntityListener.class) @Getter @EqualsAndHashCode -@ToString public class Purchase { @Id @UuidGenerator @@ -44,6 +45,9 @@ public class Purchase { @JoinColumn(name = "member_id", foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT)) private Member member; + @OneToMany(mappedBy = "purchase") + private List purchaseItems = new ArrayList<>(); + @CreatedDate @Temporal(TemporalType.TIMESTAMP) @Column(name = "created_at", nullable = false) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java index 19ed1efb..36d1a244 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java @@ -55,7 +55,20 @@ public class PurchaseItem { @Builder public PurchaseItem(Purchase purchase, Ticket ticket) { - this.purchase = purchase; + setPurchase(purchase); this.ticket = ticket; } + + public void setPurchase(Purchase purchase) { + System.out.println("setPurchase of purchaseitem"); + if (this.purchase != null) { + this.purchase.getPurchaseItems().remove(this); + } + this.purchase = purchase; + + if (purchase == null) { + return; + } + this.purchase.getPurchaseItems().add(this); + } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/MemberPurchaseDtoResultTransformer.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/MemberPurchaseDtoResultTransformer.java new file mode 100644 index 00000000..e72f8c34 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/MemberPurchaseDtoResultTransformer.java @@ -0,0 +1,64 @@ +package com.tiketeer.Tiketeer.domain.purchase.repository; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.hibernate.query.ResultListTransformer; + +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesResultDto; +import com.tiketeer.Tiketeer.util.UuidConverter; + +public class MemberPurchaseDtoResultTransformer implements ResultListTransformer { + + private final Map purchaseDtoMap = new LinkedHashMap<>(); + Map aliasToIndexMap = new LinkedHashMap<>(); + + public GetMemberPurchasesResultDto transformTuple(Object[] tuple) { + try { + System.out.println("transformTuple : " + Arrays.stream(tuple).map(x -> x).toList().toString()); + } catch (Throwable e) { + throw new RuntimeException(e); + } + + UUID purchaseId = + UuidConverter.convertBytesToUUID( + (byte[])tuple[aliasToIndexMap.get(GetMemberPurchasesResultDto.ID_ALIAS)]); + GetMemberPurchasesResultDto purchaseDto = purchaseDtoMap.computeIfAbsent( + purchaseId, + id -> new GetMemberPurchasesResultDto(tuple, aliasToIndexMap) + ); + purchaseDto.getPurchaseItems().add( + new GetMemberPurchasesResultDto.PurchaseItem(tuple, aliasToIndexMap) + ); + + return purchaseDto; + } + + private Map aliasToIndexMap(List aliases) { + Map aliasToIndexMap = new LinkedHashMap<>(); + + for (int i = 0; i < aliases.size(); i++) { + aliasToIndexMap.put( + aliases.get(i), + i + ); + } + + return aliasToIndexMap; + } + + @Override + public List transformList(List list) { + List aliases = Arrays.asList("p_id", "tg_id", "tg_title", "tg_location", "tg_eventTime", "tg_createdAt", + "tg_category", "tg_runningMinutes", "pi_id", "t_id", "t_title", "t_description", "t_price"); + aliasToIndexMap = aliasToIndexMap(aliases); + for (var tuple : list) { + transformTuple((Object[])tuple); + } + return new ArrayList<>(purchaseDtoMap.values()); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepository.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepository.java index b2c2640a..cc09832d 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepository.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepository.java @@ -5,8 +5,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import com.tiketeer.Tiketeer.domain.purchase.Purchase; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; @Repository -public interface PurchaseItemRepository extends JpaRepository { +public interface PurchaseItemRepository extends JpaRepository { } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepository.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepository.java index d2f4af86..0992dca2 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepository.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepository.java @@ -9,15 +9,16 @@ import org.springframework.stereotype.Repository; import com.tiketeer.Tiketeer.domain.member.Member; -import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesResultDto; +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchase; import com.tiketeer.Tiketeer.domain.purchase.Purchase; @Repository public interface PurchaseRepository extends JpaRepository { @Query( - "SELECT new com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesResultDto(p.id, tg.id, tg.title, tg.location, tg.eventTime, tg.saleStart, tg.saleEnd, p.createdAt, tg.category, sum(t.price), count(*)) " - + "FROM Purchase p LEFT JOIN PurchaseItem pi ON p = pi.purchase LEFT JOIN Ticket t ON t = pi.ticket LEFT JOIN Ticketing tg ON t.ticketing = tg " - + "WHERE p.member = :member GROUP BY p, t ORDER BY p.createdAt, p.id" + value = "SELECT p " + + "FROM Purchase p LEFT JOIN fetch p.purchaseItems pi LEFT JOIN fetch pi.ticket t LEFT JOIN fetch t.ticketing tg " + + "WHERE p.member = :member ORDER BY p.createdAt, p.id" ) - List findWithTicketingByMember(@Param("member") Member member); + List findWithTicketingByMember(@Param("member") Member member); + } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImpl.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImpl.java new file mode 100644 index 00000000..2481d8e4 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImpl.java @@ -0,0 +1,39 @@ +package com.tiketeer.Tiketeer.domain.purchase.repository; + +import java.util.List; + +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import com.tiketeer.Tiketeer.domain.member.Member; +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesResultDto; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; + +@Repository +public class PurchaseRepositoryImpl { + @PersistenceContext + private EntityManager entityManager; + + public List findWithTicketingByMember( + @Param("member") Member member) { + return entityManager.createNativeQuery(""" + SELECT p.purchase_id as p_id, tg.ticketing_id as tg_id, tg.title as tg_title, + tg.location as tg_location, tg.event_time as tg_eventTime, tg.created_at as tg_createdAt, + tg.category as tg_category, tg.running_minutes as tg_runningMinutes, + pi.purchase_item_id as pi_id, t.ticket_id as t_id, t.title as t_title, + t.description as t_description, t.price as t_price + FROM purchases p + LEFT JOIN purchase_items pi ON p.purchase_id = pi.purchase_id + LEFT JOIN tickets t ON pi.ticket_id = t.ticket_id + LEFT JOIN ticketings tg ON t.ticketing_id = tg.ticketing_id + WHERE p.member_id = :memberId + ORDER BY p.created_at, p.purchase_id + """) + .setParameter("memberId", member.getId()) + .unwrap(org.hibernate.query.Query.class) + .setResultListTransformer(new MemberPurchaseDtoResultTransformer()) + .getResultList(); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java index a25d9b38..7d158ca2 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java @@ -73,11 +73,12 @@ public class Ticket { private Integer version; @Builder - public Ticket(long price, int stock, int remainingStock, Ticketing ticketing) { + public Ticket(long price, int stock, int remainingStock, String title, String description, Ticketing ticketing) { this.price = price; this.stock = stock; this.remainingStock = remainingStock; + this.title = title; + this.description = description; this.ticketing = ticketing; } - } diff --git a/src/main/java/com/tiketeer/Tiketeer/util/UuidConverter.java b/src/main/java/com/tiketeer/Tiketeer/util/UuidConverter.java new file mode 100644 index 00000000..44e71637 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/util/UuidConverter.java @@ -0,0 +1,13 @@ +package com.tiketeer.Tiketeer.util; + +import java.nio.ByteBuffer; +import java.util.UUID; + +public class UuidConverter { + public static UUID convertBytesToUUID(byte[] bytes) { + ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + long high = byteBuffer.getLong(); + long low = byteBuffer.getLong(); + return new UUID(high, low); + } +} diff --git a/src/main/resources/db/migration/V2__migration.sql b/src/main/resources/db/migration/V2__migration.sql index f55eea69..b433099f 100644 --- a/src/main/resources/db/migration/V2__migration.sql +++ b/src/main/resources/db/migration/V2__migration.sql @@ -17,7 +17,7 @@ ALTER TABLE tickets ADD COLUMN description TEXT; ALTER TABLE tickets -ADD COLUMN title varchar not null; +ADD COLUMN title varchar(255) not null; create table purchase_items ( created_at timestamp(6) not null, diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java index 8ad5aa52..439f80d7 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java @@ -36,6 +36,8 @@ import com.tiketeer.Tiketeer.domain.member.controller.dto.ResetPasswordRequestDto; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; import com.tiketeer.Tiketeer.domain.purchase.Purchase; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; import com.tiketeer.Tiketeer.domain.ticket.Ticket; @@ -66,6 +68,9 @@ class MemberControllerTest { @Autowired private ObjectMapper objectMapper; + @Autowired + private PurchaseItemRepository purchaseItemRepository; + @BeforeEach void initDB() { testHelper.initDB(); @@ -99,9 +104,9 @@ void getMemberTicketingSalesSuccess() throws Exception { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + var ticket1 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket1", "", ticketing)); + var ticket2 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket2", "", ticketing)); + var ticket3 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket3", "", ticketing)); //when - then MvcResult result = mockMvc.perform(get("/api/members/" + member.getId() + "/sale") @@ -364,10 +369,11 @@ void getMemberPurchasesSuccess() throws Exception { ); var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing2)); + var ticket1 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket1", "", ticketing1)); + var ticket2 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket2", "", ticketing2)); + + purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); + purchaseItemRepository.save(new PurchaseItem(purchase2, ticket2)); // when var result = mockMvc.perform(get("/api/members/{memberId}/purchases", member.getId()) @@ -396,8 +402,6 @@ void getMemberPurchasesSuccess() throws Exception { Assertions.assertThat(findPurchase2.size()).isEqualTo(1); Assertions.assertThat(findPurchase1.getFirst().getTitle()).isEqualTo(ticketing1.getTitle()); Assertions.assertThat(findPurchase2.getFirst().getTitle()).isEqualTo(ticketing2.getTitle()); - Assertions.assertThat(findPurchase1.getFirst().getCount()).isEqualTo(2); - Assertions.assertThat(findPurchase2.getFirst().getCount()).isEqualTo(1); } @Test diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java index 570541b3..3bdf1e6c 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java @@ -15,6 +15,8 @@ import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesCommandDto; import com.tiketeer.Tiketeer.domain.purchase.Purchase; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; @@ -38,6 +40,9 @@ public class GetMemberPurchasesUseCaseTest { @Autowired private TicketRepository ticketRepository; + @Autowired + private PurchaseItemRepository purchaseItemRepository; + @BeforeEach void initTable() { testHelper.initDB(); @@ -84,10 +89,14 @@ void getMemberPurchases() { ); var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing2)); + var ticket1 = ticketRepository.save(new Ticket(1000, 4, 4, "ticket1", "", ticketing1)); + var ticket2 = ticketRepository.save(new Ticket(1000, 3, 3, "ticket2", "", ticketing2)); + var ticket3 = ticketRepository.save(new Ticket(1000, 3, 3, "ticket3", "", ticketing2)); + + purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); + purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); + purchaseItemRepository.save(new PurchaseItem(purchase2, ticket2)); + purchaseItemRepository.save(new PurchaseItem(purchase2, ticket3)); // when var results = getMemberPurchasesUseCase.getMemberPurchases( @@ -105,8 +114,8 @@ void getMemberPurchases() { Assertions.assertThat(findPurchase2.size()).isEqualTo(1); Assertions.assertThat(findPurchase1.getFirst().getTitle()).isEqualTo(ticketing1.getTitle()); Assertions.assertThat(findPurchase2.getFirst().getTitle()).isEqualTo(ticketing2.getTitle()); - Assertions.assertThat(findPurchase1.getFirst().getCount()).isEqualTo(2); - Assertions.assertThat(findPurchase2.getFirst().getCount()).isEqualTo(1); + Assertions.assertThat(findPurchase1.getFirst().getPurchaseItems().size()).isEqualTo(2); + Assertions.assertThat(findPurchase2.getFirst().getPurchaseItems().size()).isEqualTo(2); } @Test diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberTicketingSaleUseCaseTest.java similarity index 88% rename from src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java rename to src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberTicketingSaleUseCaseTest.java index 7dc906c0..e17e9c64 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberTicketingSaleUseCaseTest.java @@ -1,4 +1,4 @@ -package com.tiketeer.Tiketeer.domain.member.service; +package com.tiketeer.Tiketeer.domain.member.usecase; import static java.time.LocalDateTime.*; import static org.assertj.core.api.Assertions.*; @@ -13,7 +13,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; -import com.tiketeer.Tiketeer.domain.member.usecase.GetMemberTicketingSalesUseCase; import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberTicketingSalesCommandDto; import com.tiketeer.Tiketeer.domain.purchase.Purchase; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; @@ -25,7 +24,7 @@ @Import({TestHelper.class}) @SpringBootTest -class MemberTicketingServiceTest { +class GetMemberTicketingSaleUseCaseTest { @Autowired private TestHelper testHelper; @@ -69,9 +68,9 @@ void getMemberTicketingSalesSuccess() { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "ticket1", "", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "ticket2", "", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "ticket3", "", ticketing)); //when var memberTicketingSale = getMemberTicketingSalesUseCase.getMemberTicketingSales( diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImplTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImplTest.java new file mode 100644 index 00000000..ccf65ede --- /dev/null +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImplTest.java @@ -0,0 +1,171 @@ +package com.tiketeer.Tiketeer.domain.purchase.repository; + +import java.lang.reflect.Method; +import java.time.LocalDateTime; +import java.util.Arrays; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.transaction.annotation.Transactional; + +import com.tiketeer.Tiketeer.domain.member.Member; +import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; +import com.tiketeer.Tiketeer.domain.purchase.Purchase; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; +import com.tiketeer.Tiketeer.domain.role.Role; +import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; +import com.tiketeer.Tiketeer.domain.role.repository.RoleRepository; +import com.tiketeer.Tiketeer.domain.ticket.Ticket; +import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; +import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@DataJpaTest +public class PurchaseRepositoryImplTest { + @Autowired + private TicketRepository ticketRepository; + + @Autowired + private PurchaseRepository purchaseRepository; + + @Autowired + private TicketingRepository ticketingRepository; + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private RoleRepository roleRepository; + + @Autowired + private PurchaseItemRepository purchaseItemRepository; + + @Autowired + private PurchaseRepositoryImpl purchaseRepositoryImpl; + + @Test + @DisplayName("멤버 구매 내역 생성 > 멤버 구매 내역 + 티켓 정보와 함께 조회 > 성공") + @Transactional + void findWithTicketingByMember() throws Throwable { + var mockEmail = "test@test.com"; + var role = roleRepository.save(new Role(RoleEnum.SELLER)); + var member = memberRepository.save( + new Member(mockEmail, "asdf1234", 0L, false, null, role)); + var now = LocalDateTime.now(); + var ticketing1 = ticketingRepository.save( + Ticketing.builder() + .member(member) + .description("") + .title("test1") + .location("Seoul") + .eventTime(now) + .category("") + .runningMinutes(600) + .saleStart(now) + .saleEnd(now) + .build() + ); + var ticketing2 = ticketingRepository.save( + Ticketing.builder() + .member(member) + .description("") + .title("test2") + .location("Seoul") + .eventTime(now) + .category("") + .runningMinutes(600) + .saleStart(now) + .saleEnd(now) + .build() + ); + + var ticket1 = ticketRepository.save(new Ticket(1000, 3, 3, "ticket1", "", ticketing1)); + var ticket2 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket2", "", ticketing2)); + + var purchase1 = purchaseRepository.save(new Purchase(member)); + var purchase2 = purchaseRepository.save(new Purchase(member)); + + var purchaseItem1 = purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); + var purchaseItem2 = purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); + var purchaseItem3 = purchaseItemRepository.save(new PurchaseItem(purchase2, ticket2)); + + // when + var m = memberRepository.findByEmail(mockEmail).orElseThrow(); + var results = purchaseRepositoryImpl.findWithTicketingByMember(m); + + // then + Assertions.assertThat(results.size()).isEqualTo(2); + Assertions.assertThat(results.get(0).getPurchaseId()).isEqualTo(purchase1.getId()); + Assertions.assertThat(results.get(1).getPurchaseId()).isEqualTo(purchase2.getId()); + + var purchasePropertyNames = Arrays.asList("ticketingId", "title", "location", "eventTime", + "createdAt", "category", "runningMinutes"); + for (int i = 0; i < 2; i++) { + var purchase = results.get(i); + var ticketing = i == 0 ? ticketing1 : ticketing2; + for (var p : purchasePropertyNames) { + var comparablePropertyName = p.equals("ticketingId") ? "id" : p; + checkObjectProperty(purchase, p, ticketing, comparablePropertyName); + } + } + + Assertions.assertThat(results.get(0).getPurchaseItems().size()).isEqualTo(2); + if (results.get(0).getPurchaseItems().getFirst().getPurchaseItemId().compareTo(purchaseItem1.getId()) == 0) { + Assertions.assertThat(results.getFirst().getPurchaseItems().get(1).getPurchaseItemId()) + .isEqualTo(purchaseItem2.getId()); + } else { + Assertions.assertThat(results.getFirst().getPurchaseItems().get(0).getPurchaseItemId()) + .isEqualTo(purchaseItem2.getId()); + Assertions.assertThat(results.getFirst().getPurchaseItems().get(1).getPurchaseItemId()) + .isEqualTo(purchaseItem1.getId()); + } + Assertions.assertThat(results.get(1).getPurchaseItems().getFirst().getPurchaseItemId()) + .isEqualTo(purchaseItem3.getId()); + + var purchaseItemPropertyNames = Arrays.asList("ticketId", "title", "description", "price"); + for (int i = 0; i < 2; i++) { + var purchase = results.get(i); + var ticket = i == 0 ? ticket1 : ticket2; + for (var p : purchaseItemPropertyNames) { + var comparablePropertyName = p.equals("ticketId") ? "id" : p; + checkObjectProperty(purchase.getPurchaseItems().getFirst(), p, ticket, comparablePropertyName); + if (i == 0) { + checkObjectProperty(purchase.getPurchaseItems().get(1), p, ticket, comparablePropertyName); + } + } + } + } + + private void checkObjectProperty(Object object, String propertyName, Object comparableObj, + String comparablePropertyName) throws Throwable { + if (propertyName.isEmpty() || comparablePropertyName.isEmpty()) { + throw new Error("no property name"); + } + var methodName = "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + Method method = object + .getClass() + .getMethod(methodName); + + var methodName2 = propertyName.equals(comparablePropertyName) ? methodName : + "get" + comparablePropertyName.substring(0, 1).toUpperCase() + comparablePropertyName.substring(1); + Method method2 = comparableObj.getClass().getMethod(methodName2); + + // 메소드 호출 + Object property = method.invoke(object); + Object comparableProperty = method2.invoke(comparableObj); + log.debug( + "checkObjectProperty : " + property + "(" + methodName + "), " + comparableProperty + "(" + methodName2 + + ")"); + if (property instanceof LocalDateTime time && comparableProperty instanceof LocalDateTime comparableTime) { + Assertions.assertThat(time).isEqualToIgnoringNanos(comparableTime); + } else { + Assertions.assertThat(property).isEqualTo(comparableProperty); + } + } +} diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java index 8f466e42..dec32909 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java @@ -1,6 +1,8 @@ package com.tiketeer.Tiketeer.domain.purchase.repository; +import java.lang.reflect.Method; import java.time.LocalDateTime; +import java.util.Arrays; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; @@ -12,6 +14,7 @@ import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; import com.tiketeer.Tiketeer.domain.purchase.Purchase; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; import com.tiketeer.Tiketeer.domain.role.Role; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; import com.tiketeer.Tiketeer.domain.role.repository.RoleRepository; @@ -20,6 +23,9 @@ import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; +import lombok.extern.slf4j.Slf4j; + +@Slf4j @DataJpaTest public class PurchaseRepositoryTest { @Autowired @@ -37,10 +43,13 @@ public class PurchaseRepositoryTest { @Autowired private RoleRepository roleRepository; + @Autowired + private PurchaseItemRepository purchaseItemRepository; + @Test @DisplayName("멤버 구매 내역 생성 > 멤버 구매 내역 + 티켓 정보와 함께 조회 > 성공") @Transactional - void findWithTicketingByMember() { + void findWithTicketingByMember() throws Throwable { var mockEmail = "test@test.com"; var role = roleRepository.save(new Role(RoleEnum.SELLER)); var member = memberRepository.save( @@ -72,21 +81,88 @@ void findWithTicketingByMember() { .saleEnd(now) .build() ); + + var ticket1 = ticketRepository.save(new Ticket(1000, 3, 3, "ticket1", "", ticketing1)); + var ticket2 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket2", "", ticketing2)); + var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing2)); + + var purchaseItem1 = purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); + var purchaseItem2 = purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); + var purchaseItem3 = purchaseItemRepository.save(new PurchaseItem(purchase2, ticket2)); // when - var results = purchaseRepository.findWithTicketingByMember(member); + var m = memberRepository.findByEmail(mockEmail).orElseThrow(); + var results = purchaseRepository.findWithTicketingByMember(m); // then Assertions.assertThat(results.size()).isEqualTo(2); - Assertions.assertThat(results.get(0).getCount()).isEqualTo(2); - Assertions.assertThat(results.get(0).getTicketingId()).isEqualTo(ticketing1.getId()); - Assertions.assertThat(results.get(1).getCount()).isEqualTo(1); - Assertions.assertThat(results.get(1).getTicketingId()).isEqualTo(ticketing2.getId()); + Assertions.assertThat(results.get(0).getPurchaseId()).isEqualTo(purchase1.getId()); + Assertions.assertThat(results.get(1).getPurchaseId()).isEqualTo(purchase2.getId()); + + var purchasePropertyNames = Arrays.asList("ticketingId", "title", "location", "eventTime", + "createdAt", "category", "runningMinutes"); + for (int i = 0; i < 2; i++) { + var purchase = results.get(i); + var ticketing = i == 0 ? ticketing1 : ticketing2; + for (var p : purchasePropertyNames) { + var comparablePropertyName = p.equals("ticketingId") ? "id" : p; + checkObjectProperty(purchase, p, ticketing, comparablePropertyName); + } + } + + Assertions.assertThat(results.get(0).getPurchaseItems().size()).isEqualTo(2); + if (results.get(0).getPurchaseItems().getFirst().getPurchaseItemId().compareTo(purchaseItem1.getId()) == 0) { + Assertions.assertThat(results.getFirst().getPurchaseItems().get(1).getPurchaseItemId()) + .isEqualTo(purchaseItem2.getId()); + } else { + Assertions.assertThat(results.getFirst().getPurchaseItems().get(0).getPurchaseItemId()) + .isEqualTo(purchaseItem2.getId()); + Assertions.assertThat(results.getFirst().getPurchaseItems().get(1).getPurchaseItemId()) + .isEqualTo(purchaseItem1.getId()); + } + Assertions.assertThat(results.get(1).getPurchaseItems().getFirst().getPurchaseItemId()) + .isEqualTo(purchaseItem3.getId()); + + var purchaseItemPropertyNames = Arrays.asList("ticketId", "title", "description", "price"); + for (int i = 0; i < 2; i++) { + var purchase = results.get(i); + var ticket = i == 0 ? ticket1 : ticket2; + for (var p : purchaseItemPropertyNames) { + var comparablePropertyName = p.equals("ticketId") ? "id" : p; + checkObjectProperty(purchase.getPurchaseItems().getFirst(), p, ticket, comparablePropertyName); + if (i == 0) { + checkObjectProperty(purchase.getPurchaseItems().get(1), p, ticket, comparablePropertyName); + } + } + } + } + + private void checkObjectProperty(Object object, String propertyName, Object comparableObj, + String comparablePropertyName) throws Throwable { + if (propertyName.isEmpty() || comparablePropertyName.isEmpty()) { + throw new Error("no property name"); + } + var methodName = "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + Method method = object + .getClass() + .getMethod(methodName); + + var methodName2 = propertyName.equals(comparablePropertyName) ? methodName : + "get" + comparablePropertyName.substring(0, 1).toUpperCase() + comparablePropertyName.substring(1); + Method method2 = comparableObj.getClass().getMethod(methodName2); + + // 메소드 호출 + Object property = method.invoke(object); + Object comparableProperty = method2.invoke(comparableObj); + log.debug( + "checkObjectProperty : " + property + "(" + methodName + "), " + comparableProperty + "(" + methodName2 + + ")"); + if (property instanceof LocalDateTime time && comparableProperty instanceof LocalDateTime comparableTime) { + Assertions.assertThat(time).isEqualToIgnoringNanos(comparableTime); + } else { + Assertions.assertThat(property).isEqualTo(comparableProperty); + } } } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java index c5188605..34df8833 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java @@ -64,9 +64,9 @@ void findTicketingWithTicketStock() { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "ticket1", "", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "ticket2", "", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "ticket3", "", ticketing)); //when var memberTicketingSale = ticketingRepository.findTicketingWithTicketStock(member.getEmail()).getFirst(); @@ -104,7 +104,7 @@ void findAllTicketingsWithRemainStockSuccess() { ); var stock1 = 3; for (int i = 0; i < stock1; i++) { - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "ticket1", "", ticketing1)); } var ticketing2 = ticketingRepository.save( Ticketing.builder() @@ -121,7 +121,7 @@ void findAllTicketingsWithRemainStockSuccess() { ); var stock2 = 1; for (int i = 0; i < stock2; i++) { - ticketRepository.save(new Ticket(1000, 1, 1, ticketing2)); + ticketRepository.save(new Ticket(1000, 1, 1, "ticket2", "", ticketing2)); } //when @@ -173,10 +173,10 @@ void findTicketingWithRemainStock() { var stock = 3; var remainStock = 1; for (int i = 0; i < stock - remainStock; i++) { - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "ticket1", "", ticketing)); } for (int i = 0; i < remainStock; i++) { - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "ticket2", "", ticketing)); } //when diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java index bd1d557c..2091388a 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java @@ -91,7 +91,7 @@ private List createTicketings(Member member, int count) { .build() ).toList(); ticketings.forEach(ticketing -> { - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "ticket1", "", ticketing)); }); return ticketingRepository.saveAll(ticketings); } From ccfc1b7df59d77a2e9d5318640534a679392b7ed Mon Sep 17 00:00:00 2001 From: dla0510 Date: Fri, 7 Jun 2024 23:21:27 +0900 Subject: [PATCH 06/38] =?UTF-8?q?getMemberPurchase=20with=20ResultListSetT?= =?UTF-8?q?ransformer=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/GetMemberPurchasesResultDto.java | 29 --- .../MemberPurchaseDtoResultTransformer.java | 64 ------- .../repository/PurchaseRepositoryImpl.java | 39 ---- .../tiketeer/Tiketeer/util/UuidConverter.java | 13 -- .../PurchaseRepositoryImplTest.java | 171 ------------------ 5 files changed, 316 deletions(-) delete mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/MemberPurchaseDtoResultTransformer.java delete mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImpl.java delete mode 100644 src/main/java/com/tiketeer/Tiketeer/util/UuidConverter.java delete mode 100644 src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImplTest.java diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesResultDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesResultDto.java index 40d04d41..5dac3b9f 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesResultDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesResultDto.java @@ -1,14 +1,9 @@ package com.tiketeer.Tiketeer.domain.member.usecase.dto; -import java.sql.Timestamp; import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.UUID; -import com.tiketeer.Tiketeer.util.UuidConverter; - import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -27,8 +22,6 @@ public final class GetMemberPurchasesResultDto { private final int runningMinutes; private final List purchaseItems; - public static final String ID_ALIAS = "p_id"; - @Builder public GetMemberPurchasesResultDto(UUID purchaseId, UUID ticketingId, String title, String location, LocalDateTime eventTime, LocalDateTime createdAt, String category, @@ -44,19 +37,6 @@ public GetMemberPurchasesResultDto(UUID purchaseId, UUID ticketingId, String tit this.purchaseItems = purchaseItems; } - @Builder - public GetMemberPurchasesResultDto(Object[] tuple, Map aliasToIndexMap) { - this.purchaseId = UuidConverter.convertBytesToUUID((byte[])tuple[aliasToIndexMap.get("p_id")]); - this.ticketingId = UuidConverter.convertBytesToUUID((byte[])tuple[aliasToIndexMap.get("tg_id")]); - this.title = (String)tuple[aliasToIndexMap.get("tg_title")]; - this.location = (String)tuple[aliasToIndexMap.get("tg_location")]; - this.eventTime = ((Timestamp)tuple[aliasToIndexMap.get("tg_eventTime")]).toLocalDateTime(); - this.createdAt = ((Timestamp)tuple[aliasToIndexMap.get("tg_createdAt")]).toLocalDateTime(); - this.category = (String)tuple[aliasToIndexMap.get("tg_category")]; - this.runningMinutes = (int)tuple[aliasToIndexMap.get("tg_runningMinutes")]; - this.purchaseItems = new ArrayList<>(); - } - @Builder public GetMemberPurchasesResultDto(GetMemberPurchase purchase) { this.purchaseId = purchase.getPurchaseId(); @@ -90,15 +70,6 @@ public PurchaseItem(UUID purchaseItemId, UUID ticketId, String title, String des this.price = price; } - @Builder - public PurchaseItem(Object[] tuple, Map aliasToIndexMap) { - this.purchaseItemId = UuidConverter.convertBytesToUUID((byte[])tuple[aliasToIndexMap.get("pi_id")]); - this.ticketId = UuidConverter.convertBytesToUUID((byte[])tuple[aliasToIndexMap.get("t_id")]); - this.title = (String)tuple[aliasToIndexMap.get("t_title")]; - this.description = (String)tuple[aliasToIndexMap.get("t_description")]; - this.price = (Long)tuple[aliasToIndexMap.get("t_price")]; - } - @Builder public PurchaseItem(GetMemberPurchase.GetMemberPurchaseItems pi) { this.purchaseItemId = pi.getPurchaseItemId(); diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/MemberPurchaseDtoResultTransformer.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/MemberPurchaseDtoResultTransformer.java deleted file mode 100644 index e72f8c34..00000000 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/MemberPurchaseDtoResultTransformer.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.tiketeer.Tiketeer.domain.purchase.repository; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.hibernate.query.ResultListTransformer; - -import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesResultDto; -import com.tiketeer.Tiketeer.util.UuidConverter; - -public class MemberPurchaseDtoResultTransformer implements ResultListTransformer { - - private final Map purchaseDtoMap = new LinkedHashMap<>(); - Map aliasToIndexMap = new LinkedHashMap<>(); - - public GetMemberPurchasesResultDto transformTuple(Object[] tuple) { - try { - System.out.println("transformTuple : " + Arrays.stream(tuple).map(x -> x).toList().toString()); - } catch (Throwable e) { - throw new RuntimeException(e); - } - - UUID purchaseId = - UuidConverter.convertBytesToUUID( - (byte[])tuple[aliasToIndexMap.get(GetMemberPurchasesResultDto.ID_ALIAS)]); - GetMemberPurchasesResultDto purchaseDto = purchaseDtoMap.computeIfAbsent( - purchaseId, - id -> new GetMemberPurchasesResultDto(tuple, aliasToIndexMap) - ); - purchaseDto.getPurchaseItems().add( - new GetMemberPurchasesResultDto.PurchaseItem(tuple, aliasToIndexMap) - ); - - return purchaseDto; - } - - private Map aliasToIndexMap(List aliases) { - Map aliasToIndexMap = new LinkedHashMap<>(); - - for (int i = 0; i < aliases.size(); i++) { - aliasToIndexMap.put( - aliases.get(i), - i - ); - } - - return aliasToIndexMap; - } - - @Override - public List transformList(List list) { - List aliases = Arrays.asList("p_id", "tg_id", "tg_title", "tg_location", "tg_eventTime", "tg_createdAt", - "tg_category", "tg_runningMinutes", "pi_id", "t_id", "t_title", "t_description", "t_price"); - aliasToIndexMap = aliasToIndexMap(aliases); - for (var tuple : list) { - transformTuple((Object[])tuple); - } - return new ArrayList<>(purchaseDtoMap.values()); - } -} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImpl.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImpl.java deleted file mode 100644 index 2481d8e4..00000000 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.tiketeer.Tiketeer.domain.purchase.repository; - -import java.util.List; - -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -import com.tiketeer.Tiketeer.domain.member.Member; -import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesResultDto; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; - -@Repository -public class PurchaseRepositoryImpl { - @PersistenceContext - private EntityManager entityManager; - - public List findWithTicketingByMember( - @Param("member") Member member) { - return entityManager.createNativeQuery(""" - SELECT p.purchase_id as p_id, tg.ticketing_id as tg_id, tg.title as tg_title, - tg.location as tg_location, tg.event_time as tg_eventTime, tg.created_at as tg_createdAt, - tg.category as tg_category, tg.running_minutes as tg_runningMinutes, - pi.purchase_item_id as pi_id, t.ticket_id as t_id, t.title as t_title, - t.description as t_description, t.price as t_price - FROM purchases p - LEFT JOIN purchase_items pi ON p.purchase_id = pi.purchase_id - LEFT JOIN tickets t ON pi.ticket_id = t.ticket_id - LEFT JOIN ticketings tg ON t.ticketing_id = tg.ticketing_id - WHERE p.member_id = :memberId - ORDER BY p.created_at, p.purchase_id - """) - .setParameter("memberId", member.getId()) - .unwrap(org.hibernate.query.Query.class) - .setResultListTransformer(new MemberPurchaseDtoResultTransformer()) - .getResultList(); - } -} diff --git a/src/main/java/com/tiketeer/Tiketeer/util/UuidConverter.java b/src/main/java/com/tiketeer/Tiketeer/util/UuidConverter.java deleted file mode 100644 index 44e71637..00000000 --- a/src/main/java/com/tiketeer/Tiketeer/util/UuidConverter.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.tiketeer.Tiketeer.util; - -import java.nio.ByteBuffer; -import java.util.UUID; - -public class UuidConverter { - public static UUID convertBytesToUUID(byte[] bytes) { - ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); - long high = byteBuffer.getLong(); - long low = byteBuffer.getLong(); - return new UUID(high, low); - } -} diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImplTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImplTest.java deleted file mode 100644 index ccf65ede..00000000 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryImplTest.java +++ /dev/null @@ -1,171 +0,0 @@ -package com.tiketeer.Tiketeer.domain.purchase.repository; - -import java.lang.reflect.Method; -import java.time.LocalDateTime; -import java.util.Arrays; - -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.transaction.annotation.Transactional; - -import com.tiketeer.Tiketeer.domain.member.Member; -import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; -import com.tiketeer.Tiketeer.domain.purchase.Purchase; -import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; -import com.tiketeer.Tiketeer.domain.role.Role; -import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; -import com.tiketeer.Tiketeer.domain.role.repository.RoleRepository; -import com.tiketeer.Tiketeer.domain.ticket.Ticket; -import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; -import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@DataJpaTest -public class PurchaseRepositoryImplTest { - @Autowired - private TicketRepository ticketRepository; - - @Autowired - private PurchaseRepository purchaseRepository; - - @Autowired - private TicketingRepository ticketingRepository; - - @Autowired - private MemberRepository memberRepository; - - @Autowired - private RoleRepository roleRepository; - - @Autowired - private PurchaseItemRepository purchaseItemRepository; - - @Autowired - private PurchaseRepositoryImpl purchaseRepositoryImpl; - - @Test - @DisplayName("멤버 구매 내역 생성 > 멤버 구매 내역 + 티켓 정보와 함께 조회 > 성공") - @Transactional - void findWithTicketingByMember() throws Throwable { - var mockEmail = "test@test.com"; - var role = roleRepository.save(new Role(RoleEnum.SELLER)); - var member = memberRepository.save( - new Member(mockEmail, "asdf1234", 0L, false, null, role)); - var now = LocalDateTime.now(); - var ticketing1 = ticketingRepository.save( - Ticketing.builder() - .member(member) - .description("") - .title("test1") - .location("Seoul") - .eventTime(now) - .category("") - .runningMinutes(600) - .saleStart(now) - .saleEnd(now) - .build() - ); - var ticketing2 = ticketingRepository.save( - Ticketing.builder() - .member(member) - .description("") - .title("test2") - .location("Seoul") - .eventTime(now) - .category("") - .runningMinutes(600) - .saleStart(now) - .saleEnd(now) - .build() - ); - - var ticket1 = ticketRepository.save(new Ticket(1000, 3, 3, "ticket1", "", ticketing1)); - var ticket2 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket2", "", ticketing2)); - - var purchase1 = purchaseRepository.save(new Purchase(member)); - var purchase2 = purchaseRepository.save(new Purchase(member)); - - var purchaseItem1 = purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); - var purchaseItem2 = purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); - var purchaseItem3 = purchaseItemRepository.save(new PurchaseItem(purchase2, ticket2)); - - // when - var m = memberRepository.findByEmail(mockEmail).orElseThrow(); - var results = purchaseRepositoryImpl.findWithTicketingByMember(m); - - // then - Assertions.assertThat(results.size()).isEqualTo(2); - Assertions.assertThat(results.get(0).getPurchaseId()).isEqualTo(purchase1.getId()); - Assertions.assertThat(results.get(1).getPurchaseId()).isEqualTo(purchase2.getId()); - - var purchasePropertyNames = Arrays.asList("ticketingId", "title", "location", "eventTime", - "createdAt", "category", "runningMinutes"); - for (int i = 0; i < 2; i++) { - var purchase = results.get(i); - var ticketing = i == 0 ? ticketing1 : ticketing2; - for (var p : purchasePropertyNames) { - var comparablePropertyName = p.equals("ticketingId") ? "id" : p; - checkObjectProperty(purchase, p, ticketing, comparablePropertyName); - } - } - - Assertions.assertThat(results.get(0).getPurchaseItems().size()).isEqualTo(2); - if (results.get(0).getPurchaseItems().getFirst().getPurchaseItemId().compareTo(purchaseItem1.getId()) == 0) { - Assertions.assertThat(results.getFirst().getPurchaseItems().get(1).getPurchaseItemId()) - .isEqualTo(purchaseItem2.getId()); - } else { - Assertions.assertThat(results.getFirst().getPurchaseItems().get(0).getPurchaseItemId()) - .isEqualTo(purchaseItem2.getId()); - Assertions.assertThat(results.getFirst().getPurchaseItems().get(1).getPurchaseItemId()) - .isEqualTo(purchaseItem1.getId()); - } - Assertions.assertThat(results.get(1).getPurchaseItems().getFirst().getPurchaseItemId()) - .isEqualTo(purchaseItem3.getId()); - - var purchaseItemPropertyNames = Arrays.asList("ticketId", "title", "description", "price"); - for (int i = 0; i < 2; i++) { - var purchase = results.get(i); - var ticket = i == 0 ? ticket1 : ticket2; - for (var p : purchaseItemPropertyNames) { - var comparablePropertyName = p.equals("ticketId") ? "id" : p; - checkObjectProperty(purchase.getPurchaseItems().getFirst(), p, ticket, comparablePropertyName); - if (i == 0) { - checkObjectProperty(purchase.getPurchaseItems().get(1), p, ticket, comparablePropertyName); - } - } - } - } - - private void checkObjectProperty(Object object, String propertyName, Object comparableObj, - String comparablePropertyName) throws Throwable { - if (propertyName.isEmpty() || comparablePropertyName.isEmpty()) { - throw new Error("no property name"); - } - var methodName = "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); - Method method = object - .getClass() - .getMethod(methodName); - - var methodName2 = propertyName.equals(comparablePropertyName) ? methodName : - "get" + comparablePropertyName.substring(0, 1).toUpperCase() + comparablePropertyName.substring(1); - Method method2 = comparableObj.getClass().getMethod(methodName2); - - // 메소드 호출 - Object property = method.invoke(object); - Object comparableProperty = method2.invoke(comparableObj); - log.debug( - "checkObjectProperty : " + property + "(" + methodName + "), " + comparableProperty + "(" + methodName2 - + ")"); - if (property instanceof LocalDateTime time && comparableProperty instanceof LocalDateTime comparableTime) { - Assertions.assertThat(time).isEqualToIgnoringNanos(comparableTime); - } else { - Assertions.assertThat(property).isEqualTo(comparableProperty); - } - } -} From 02976fcff7d276c4eae1692abdd68d5ed82b98da Mon Sep 17 00:00:00 2001 From: dla0510 Date: Mon, 10 Jun 2024 09:39:07 +0900 Subject: [PATCH 07/38] =?UTF-8?q?=EC=BD=98=EC=86=94=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java index 36d1a244..e0592a22 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseItem.java @@ -60,7 +60,6 @@ public PurchaseItem(Purchase purchase, Ticket ticket) { } public void setPurchase(Purchase purchase) { - System.out.println("setPurchase of purchaseitem"); if (this.purchase != null) { this.purchase.getPurchaseItems().remove(this); } From a5edb5e46a94a06086d32996c056f784fd86ceff Mon Sep 17 00:00:00 2001 From: dla0510 Date: Mon, 10 Jun 2024 09:46:20 +0900 Subject: [PATCH 08/38] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/controller/MemberControllerTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java index 439f80d7..b472e935 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java @@ -370,10 +370,12 @@ void getMemberPurchasesSuccess() throws Exception { var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); var ticket1 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket1", "", ticketing1)); - var ticket2 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket2", "", ticketing2)); + var ticket2 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket2", "", ticketing1)); + var ticket3 = ticketRepository.save(new Ticket(1000, 2, 2, "ticket3", "", ticketing2)); purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); - purchaseItemRepository.save(new PurchaseItem(purchase2, ticket2)); + purchaseItemRepository.save(new PurchaseItem(purchase1, ticket2)); + purchaseItemRepository.save(new PurchaseItem(purchase2, ticket3)); // when var result = mockMvc.perform(get("/api/members/{memberId}/purchases", member.getId()) @@ -402,6 +404,8 @@ void getMemberPurchasesSuccess() throws Exception { Assertions.assertThat(findPurchase2.size()).isEqualTo(1); Assertions.assertThat(findPurchase1.getFirst().getTitle()).isEqualTo(ticketing1.getTitle()); Assertions.assertThat(findPurchase2.getFirst().getTitle()).isEqualTo(ticketing2.getTitle()); + Assertions.assertThat(findPurchase1.getFirst().getPurchaseItems().size()).isEqualTo(2); + Assertions.assertThat(findPurchase2.getFirst().getPurchaseItems().size()).isEqualTo(1); } @Test From f699351b1910725f9f7ada43dd56c73d3097bf4a Mon Sep 17 00:00:00 2001 From: dla0510 Date: Mon, 10 Jun 2024 15:57:40 +0900 Subject: [PATCH 09/38] =?UTF-8?q?createPurchase=20EP=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/PostPurchaseRequestDto.java | 11 ++- .../usecase/CreatePurchaseUseCase.java | 4 +- .../usecase/CreatePurchaseUseCaseCore.java | 44 ++++++--- .../decorator/CreatePurchaseUseCaseOLock.java | 2 +- .../decorator/CreatePurchaseUseCasePLock.java | 2 +- .../usecase/dto/CreatePurchaseCommandDto.java | 20 ++++- .../ticket/repository/TicketRepository.java | 10 +++ .../Tiketeer/configuration/AppConfig.java | 5 +- .../domain/purchase/PurchaseTestHelper.java | 31 +++---- .../controller/PurchaseControllerTest.java | 36 +++++--- .../CreatePurchaseConcurrencyTest.java | 10 ++- ...UseCaseDistributedLockConcurrencyTest.java | 3 +- ...eUseCaseOptimisticLockConcurrencyTest.java | 3 +- ...UseCasePessimisticLockConcurrencyTest.java | 3 +- .../usecase/CreatePurchaseUseCaseTest.java | 89 ++++++++++--------- .../domain/ticketing/TicketingTestHelper.java | 28 +++--- 16 files changed, 195 insertions(+), 106 deletions(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/PostPurchaseRequestDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/PostPurchaseRequestDto.java index 9eadc81d..4adcff79 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/PostPurchaseRequestDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/PostPurchaseRequestDto.java @@ -1,9 +1,11 @@ package com.tiketeer.Tiketeer.domain.purchase.controller.dto; +import java.util.List; import java.util.UUID; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseCommandDto; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; @@ -18,20 +20,21 @@ public class PostPurchaseRequestDto { private final UUID ticketingId; @NotNull - private final Integer count; + @Valid + private final List tickets; @Builder public PostPurchaseRequestDto(@NotNull UUID ticketingId, - @NotNull Integer count) { + @NotNull List tickets) { this.ticketingId = ticketingId; - this.count = count; + this.tickets = tickets; } public CreatePurchaseCommandDto convertToDto(String memberEmail) { return CreatePurchaseCommandDto.builder().memberEmail(memberEmail) .ticketingId(this.ticketingId) - .count(this.count) + .tickets(this.tickets) .build(); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCase.java index cf57700e..2d448b49 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCase.java @@ -3,8 +3,6 @@ import java.util.List; import java.util.UUID; -import org.springframework.data.domain.Limit; - import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseCommandDto; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseResultDto; import com.tiketeer.Tiketeer.domain.ticket.Ticket; @@ -19,6 +17,6 @@ default CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command, @FunctionalInterface interface ListTicketStrategy { - List findByTicketingIdAndPurchaseIsNullOrderById(UUID ticketingId, Limit limit); + List findByIdIn(List ticketIds); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java index c869ada8..087f0a7f 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java @@ -1,24 +1,32 @@ package com.tiketeer.Tiketeer.domain.purchase.usecase; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Stream; + import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Limit; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.tiketeer.Tiketeer.domain.member.service.MemberCrudService; import com.tiketeer.Tiketeer.domain.member.service.MemberPointService; import com.tiketeer.Tiketeer.domain.purchase.Purchase; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; import com.tiketeer.Tiketeer.domain.purchase.exception.NotEnoughTicketException; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.purchase.service.PurchaseService; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseCommandDto; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseResultDto; +import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingService; @Service public class CreatePurchaseUseCaseCore implements CreatePurchaseUseCase { private final PurchaseRepository purchaseRepository; + private final PurchaseItemRepository purchaseItemRepository; private final TicketingService ticketingService; private final PurchaseService purchaseService; private final MemberPointService memberPointService; @@ -28,6 +36,7 @@ public class CreatePurchaseUseCaseCore implements CreatePurchaseUseCase { @Autowired public CreatePurchaseUseCaseCore( PurchaseRepository purchaseRepository, + PurchaseItemRepository purchaseItemRepository, TicketingService ticketingService, PurchaseService purchaseService, MemberPointService memberPointService, @@ -35,6 +44,7 @@ public CreatePurchaseUseCaseCore( TicketRepository ticketRepository ) { this.purchaseRepository = purchaseRepository; + this.purchaseItemRepository = purchaseItemRepository; this.ticketingService = ticketingService; this.purchaseService = purchaseService; this.memberPointService = memberPointService; @@ -45,7 +55,7 @@ public CreatePurchaseUseCaseCore( @Override @Transactional public CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command) { - return execPurchaseLogic(command, ticketRepository::findByTicketingIdOrderById); + return execPurchaseLogic(command, ticketRepository::findByIdIn); } @Override @@ -58,24 +68,38 @@ public CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command, private CreatePurchaseResultDto execPurchaseLogic(CreatePurchaseCommandDto command, ListTicketStrategy listTicketStrategy) { var ticketingId = command.getTicketingId(); - var count = command.getCount(); var member = memberCrudService.findByEmail(command.getMemberEmail()); purchaseService.validateTicketingSalePeriod(ticketingId, command.getCommandCreatedAt()); - var ticketing = ticketingService.findById(ticketingId); - var purchase = purchaseRepository.save(Purchase.builder().member(member).build()); - var tickets = listTicketStrategy.findByTicketingIdAndPurchaseIsNullOrderById(ticketingId, Limit.of(count)); - - memberPointService.subtractPoint(member.getId(), tickets.getFirst().getPrice() * count); + var ticketIds = command.getTickets().stream().map(CreatePurchaseCommandDto.Ticket::getId).toList(); + var tickets = listTicketStrategy.findByIdIn(ticketIds); + Map ticketMap = new HashMap<>(tickets.size()); + for (var t : tickets) { + ticketMap.put(t.getId(), t); + } - if (tickets.size() < count) { - throw new NotEnoughTicketException(); + for (var t : command.getTickets()) { + var ticket = ticketMap.get(t.getId()); + var stock = ticket.getRemainingStock(); + var purchaseCount = t.getCount(); + if (stock < purchaseCount) { + throw new NotEnoughTicketException(); + } + var purchaseItems = Stream.generate(() -> new PurchaseItem(purchase, ticket)).limit(purchaseCount).toList(); + purchaseItemRepository.saveAll(purchaseItems); + ticket.setRemainingStock(stock - purchaseCount); } + var totalCost = command.getTickets() + .stream() + .mapToLong(t -> ticketMap.get(t.getId()).getPrice() * t.getCount()) + .sum(); + memberPointService.subtractPoint(member.getId(), totalCost); + return CreatePurchaseResultDto.builder() .purchaseId(purchase.getId()) .createdAt(purchase.getCreatedAt()) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCaseOLock.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCaseOLock.java index 73a98012..d6bd2e2c 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCaseOLock.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCaseOLock.java @@ -31,6 +31,6 @@ public CreatePurchaseUseCaseOLock(CreatePurchaseUseCase createPurchaseUseCase, ) public CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command) { return innerUseCase.createPurchase(command, - ticketRepository::findByTicketingIdOrderByIdWithOptimisticLock); + ticketRepository::findByIdInWithOptimisticLock); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCasePLock.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCasePLock.java index 3f530034..4a257f3c 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCasePLock.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCasePLock.java @@ -17,6 +17,6 @@ public CreatePurchaseUseCasePLock(CreatePurchaseUseCase createPurchaseUseCase, T @Override public CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command) { return innerUseCase.createPurchase(command, - ticketRepository::findByTicketingIdOrderByIdWithPessimisticLock); + ticketRepository::findByIdInWithPessimisticLock); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/CreatePurchaseCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/CreatePurchaseCommandDto.java index cf1668fb..dcb67b8d 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/CreatePurchaseCommandDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/CreatePurchaseCommandDto.java @@ -1,6 +1,7 @@ package com.tiketeer.Tiketeer.domain.purchase.usecase.dto; import java.time.LocalDateTime; +import java.util.List; import java.util.UUID; import lombok.Builder; @@ -12,17 +13,30 @@ public class CreatePurchaseCommandDto { private final String memberEmail; private final UUID ticketingId; - private final Integer count; + private final List tickets; private LocalDateTime commandCreatedAt = LocalDateTime.now(); @Builder - public CreatePurchaseCommandDto(String memberEmail, UUID ticketingId, Integer count, + public CreatePurchaseCommandDto(String memberEmail, UUID ticketingId, List tickets, LocalDateTime commandCreatedAt) { this.memberEmail = memberEmail; this.ticketingId = ticketingId; - this.count = count; + this.tickets = tickets; if (commandCreatedAt != null) { this.commandCreatedAt = commandCreatedAt; } } + + @Getter + @ToString + public static class Ticket { + private final UUID id; + private final Integer count; + + @Builder + public Ticket(UUID id, Integer count) { + this.id = id; + this.count = count; + } + } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java index 369fe91f..3a89d058 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java @@ -20,6 +20,8 @@ public interface TicketRepository extends JpaRepository { List findByTicketingIdOrderById(UUID ticketingId, Limit limit); + List findByIdIn(List ticketIds); + @Lock(LockModeType.PESSIMISTIC_WRITE) @Query("SELECT t FROM Ticket t WHERE t.ticketing.id = :ticketingId ORDER BY t.id") List findByTicketingIdOrderByIdWithPessimisticLock(UUID ticketingId, Limit limit); @@ -27,4 +29,12 @@ public interface TicketRepository extends JpaRepository { @Lock(LockModeType.OPTIMISTIC) @Query("SELECT t FROM Ticket t WHERE t.ticketing.id = :ticketingId ORDER BY FUNCTION('RAND')") List findByTicketingIdOrderByIdWithOptimisticLock(UUID ticketingId, Limit limit); + + @Lock(LockModeType.PESSIMISTIC_WRITE) + @Query("SELECT t FROM Ticket t WHERE t.id IN :ticketIds") + List findByIdInWithPessimisticLock(List ticketIds); + + @Lock(LockModeType.OPTIMISTIC) + @Query("SELECT t FROM Ticket t WHERE t.id IN :ticketIds") + List findByIdInWithOptimisticLock(List ticketIds); } diff --git a/src/test/java/com/tiketeer/Tiketeer/configuration/AppConfig.java b/src/test/java/com/tiketeer/Tiketeer/configuration/AppConfig.java index 5f357e10..2101b451 100644 --- a/src/test/java/com/tiketeer/Tiketeer/configuration/AppConfig.java +++ b/src/test/java/com/tiketeer/Tiketeer/configuration/AppConfig.java @@ -6,6 +6,7 @@ import com.tiketeer.Tiketeer.domain.member.service.MemberCrudService; import com.tiketeer.Tiketeer.domain.member.service.MemberPointService; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.purchase.service.PurchaseService; import com.tiketeer.Tiketeer.domain.purchase.usecase.CreatePurchaseUseCase; @@ -18,11 +19,13 @@ public class AppConfig { @Bean @Profile("test") public CreatePurchaseUseCase createPurchaseUseCase(PurchaseRepository purchaseRepository, + PurchaseItemRepository purchaseItemRepository, TicketingService ticketingService, PurchaseService purchaseService, MemberPointService memberPointService, MemberCrudService memberCrudService, TicketRepository ticketRepository) { - return new CreatePurchaseUseCaseCore(purchaseRepository, ticketingService, purchaseService, + return new CreatePurchaseUseCaseCore(purchaseRepository, purchaseItemRepository, ticketingService, + purchaseService, memberPointService, memberCrudService, ticketRepository); } } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseTestHelper.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseTestHelper.java index 4bd16982..04204974 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseTestHelper.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseTestHelper.java @@ -3,20 +3,20 @@ import java.util.Collections; import java.util.List; import java.util.UUID; +import java.util.stream.Stream; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.TestComponent; -import org.springframework.data.domain.Limit; import org.springframework.data.util.Pair; import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; import com.tiketeer.Tiketeer.domain.purchase.exception.NotEnoughTicketException; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.ticket.Ticket; +import com.tiketeer.Tiketeer.domain.ticket.exception.TicketNotFoundException; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; -import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; @TestComponent @@ -30,28 +30,29 @@ public class PurchaseTestHelper { private TicketRepository ticketRepository; @Autowired private TicketingRepository ticketingRepository; + @Autowired + private PurchaseItemRepository purchaseItemRepository; - public Pair> createPurchase(UUID memberId, UUID ticketingId, int count) { + public Pair> createPurchase(UUID memberId, UUID ticketId, int count) { var member = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); - var ticketing = ticketingRepository.findById(ticketingId).orElseThrow(TicketingNotFoundException::new); + var ticket = ticketRepository.findById(ticketId).orElseThrow(TicketNotFoundException::new); var purchase = purchaseRepository.save(Purchase.builder().member(member).build()); if (count > 0) { - var tickets = updateTicketPurchase(purchase, ticketing, count); - return Pair.of(purchase, tickets); + var purchaseItems = createPurchaseItems(purchase, ticket, count); + return Pair.of(purchase, purchaseItems); } return Pair.of(purchase, Collections.emptyList()); } - private List updateTicketPurchase(Purchase purchase, Ticketing ticketing, int count) { - var tickets = this.ticketRepository.findByTicketingIdOrderById(ticketing.getId(), - Limit.of(count)); - if (tickets.size() < count) { + private List createPurchaseItems(Purchase purchase, Ticket ticket, int count) { + var stock = ticket.getRemainingStock(); + if (stock < count) { throw new NotEnoughTicketException(); } - tickets.forEach(ticket -> { - this.ticketRepository.save(ticket); - }); - return tickets; + ticket.setRemainingStock(stock - count); + var purchaseItems = Stream.generate(() -> new PurchaseItem(purchase, ticket)).limit(count).toList(); + return purchaseItemRepository.saveAll(purchaseItems); + } } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseControllerTest.java index b3090ee9..6bc65fd1 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseControllerTest.java @@ -29,13 +29,14 @@ import com.tiketeer.Tiketeer.auth.constant.JwtMetadata; import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; import com.tiketeer.Tiketeer.domain.purchase.PurchaseTestHelper; import com.tiketeer.Tiketeer.domain.purchase.controller.dto.DeletePurchaseTicketsRequestDto; import com.tiketeer.Tiketeer.domain.purchase.controller.dto.PostPurchaseRequestDto; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; +import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseCommandDto; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.GetPurchaseTicketsResponseDto; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; -import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; import com.tiketeer.Tiketeer.infra.redis.RedisService; @@ -103,9 +104,11 @@ void postPurchaseSuccess() throws Exception { Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); var count = 2; + var tickets = List.of( + new CreatePurchaseCommandDto.Ticket(ticketRepository.findAll().getFirst().getId(), count)); PostPurchaseRequestDto req = PostPurchaseRequestDto.builder() .ticketingId(ticketing.getId()) - .count(count) + .tickets(tickets) .build(); String content = objectMapper.writeValueAsString(req); @@ -140,7 +143,7 @@ void postPurchaseFailPurchaseTokenIsNull() throws Exception { // given var sellerEmail = "seller@test.com"; var seller = testHelper.createMember(sellerEmail, RoleEnum.SELLER); - var ticketing = ticketingTestHelper.createTicketing(seller.getId(), 0, 3000, 2); + var ticketingId = UUID.randomUUID(); var buyerEmail = "buyer@test.com"; String token = testHelper.registerAndLoginAndReturnAccessToken(buyerEmail, RoleEnum.BUYER); @@ -149,9 +152,11 @@ void postPurchaseFailPurchaseTokenIsNull() throws Exception { Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); var count = 2; + var tickets = List.of( + new CreatePurchaseCommandDto.Ticket(UUID.randomUUID(), count)); PostPurchaseRequestDto req = PostPurchaseRequestDto.builder() - .ticketingId(ticketing.getId()) - .count(count) + .ticketingId(ticketingId) + .tickets(tickets) .build(); String content = objectMapper.writeValueAsString(req); @@ -181,9 +186,11 @@ void postPurchaseFailPurchaseTokenNotValid() throws Exception { Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); var count = 2; + var tickets = List.of( + new CreatePurchaseCommandDto.Ticket(UUID.randomUUID(), count)); PostPurchaseRequestDto req = PostPurchaseRequestDto.builder() .ticketingId(ticketingId) - .count(count) + .tickets(tickets) .build(); String content = objectMapper.writeValueAsString(req); @@ -218,9 +225,11 @@ void postPurchaseFailPurchaseTokenOfOtherUser() throws Exception { Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); var count = 2; + var tickets = List.of( + new CreatePurchaseCommandDto.Ticket(UUID.randomUUID(), count)); PostPurchaseRequestDto req = PostPurchaseRequestDto.builder() .ticketingId(ticketingId) - .count(count) + .tickets(tickets) .build(); String content = objectMapper.writeValueAsString(req); @@ -254,9 +263,11 @@ void postPurchaseFailUserNotExistInQueue() throws Exception { Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); var count = 2; + var tickets = List.of( + new CreatePurchaseCommandDto.Ticket(UUID.randomUUID(), count)); PostPurchaseRequestDto req = PostPurchaseRequestDto.builder() .ticketingId(ticketingId) - .count(count) + .tickets(tickets) .build(); String content = objectMapper.writeValueAsString(req); @@ -291,9 +302,11 @@ void postPurchaseFailNotInOrder() throws Exception { Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); var count = 2; + var tickets = List.of( + new CreatePurchaseCommandDto.Ticket(UUID.randomUUID(), count)); PostPurchaseRequestDto req = PostPurchaseRequestDto.builder() .ticketingId(ticketingId) - .count(count) + .tickets(tickets) .build(); String content = objectMapper.writeValueAsString(req); @@ -328,7 +341,8 @@ void deletePurchaseTicketsSuccess() throws Exception { Member member = memberRepository.findAll().getFirst(); Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, 1000, 2); - var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketing.getId(), 2); + var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), + ticketRepository.findAll().getFirst().getId(), 2); var purchase = purchaseTicketPair.getFirst(); var tickets = purchaseTicketPair.getSecond(); @@ -366,7 +380,7 @@ void deletePurchaseAllTicketsSuccess() throws Exception { var purchase = purchaseTicketPair.getFirst(); var tickets = purchaseTicketPair.getSecond(); - List ticketsToRefund = tickets.stream().map(Ticket::getId).toList(); + List ticketsToRefund = tickets.stream().map(PurchaseItem::getId).toList(); DeletePurchaseTicketsRequestDto req = DeletePurchaseTicketsRequestDto.builder() .purchaseId(purchase.getId()) .ticketIds(ticketsToRefund) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java index a9daace0..dc7bd6e2 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java @@ -3,6 +3,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -31,7 +32,8 @@ public class CreatePurchaseConcurrencyTest { @Autowired private TestHelper testHelper; - public int makeConcurrency(int threadNums, List buyers, Ticketing ticketing) throws InterruptedException { + public int makeConcurrency(int threadNums, List buyers, UUID ticketingId, UUID ticketId) throws + InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(threadNums); CountDownLatch startLatch = new CountDownLatch(1); CountDownLatch endLatch = new CountDownLatch(threadNums); @@ -43,10 +45,12 @@ public int makeConcurrency(int threadNums, List buyers, Ticketing ticket executorService.submit(() -> { try { startLatch.await(); + var tickets = List.of( + new CreatePurchaseCommandDto.Ticket(ticketId, 1)); createPurchaseUseCase.createPurchase(CreatePurchaseCommandDto.builder() - .ticketingId(ticketing.getId()) + .ticketingId(ticketingId) .memberEmail(buyers.get(buyerIdx).getEmail()) - .count(1) + .tickets(tickets) .build()); success.getAndIncrement(); } catch (Exception e) { diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java index b48f1cb7..94951a70 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java @@ -73,7 +73,8 @@ void createPurchaseWithConcurrency() throws InterruptedException { int threadNums = 20; var buyers = createBuyers(threadNums); - createPurchaseConcurrencyTest.makeConcurrency(threadNums, buyers, ticketing); + createPurchaseConcurrencyTest.makeConcurrency(threadNums, buyers, ticketing.getId(), + ticketRepository.findAll().getFirst().getId()); //then transaction.invoke(() -> { diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java index 5b98c1ed..e21606ad 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java @@ -67,7 +67,8 @@ void createPurchaseWithConcurrency() throws InterruptedException { int threadNums = 30; var buyers = createBuyers(threadNums); - int success = createPurchaseConcurrencyTest.makeConcurrency(threadNums, buyers, ticketing); + int success = createPurchaseConcurrencyTest.makeConcurrency(threadNums, buyers, ticketing.getId(), + ticketRepository.findAll().getFirst().getId()); //then transaction.invoke(() -> { diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java index acb30874..c0f76c54 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java @@ -67,7 +67,8 @@ void createPurchaseWithConcurrency() throws InterruptedException { int threadNums = 20; var buyers = createBuyers(threadNums); - createPurchaseConcurrencyTest.makeConcurrency(threadNums, buyers, ticketing); + createPurchaseConcurrencyTest.makeConcurrency(threadNums, buyers, ticketing.getId(), + ticketRepository.findAll().getFirst().getId()); //then transaction.invoke(() -> { diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java index 14e52bf0..bd6343ee 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java @@ -1,7 +1,6 @@ package com.tiketeer.Tiketeer.domain.purchase.usecase; -import java.time.LocalDateTime; -import java.util.Arrays; +import java.util.List; import java.util.UUID; import org.assertj.core.api.Assertions; @@ -14,30 +13,37 @@ import org.springframework.context.annotation.Import; import org.springframework.transaction.annotation.Transactional; -import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; import com.tiketeer.Tiketeer.domain.member.exception.NotEnoughPointException; import com.tiketeer.Tiketeer.domain.purchase.exception.NotEnoughTicketException; import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotInSalePeriodException; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseCommandDto; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; +import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; import com.tiketeer.Tiketeer.testhelper.TestHelper; -@Import({TestHelper.class}) +@Import({TestHelper.class, TicketingTestHelper.class}) @SpringBootTest public class CreatePurchaseUseCaseTest { @Autowired private TestHelper testHelper; @Autowired + private TicketingTestHelper ticketingTestHelper; + @Autowired private CreatePurchaseUseCase createPurchaseUseCase; @Autowired private TicketRepository ticketRepository; @Autowired private TicketingRepository ticketingRepository; + @Autowired + private PurchaseRepository purchaseRepository; + @Autowired + private PurchaseItemRepository purchaseItemRepository; @BeforeEach void initTable() { @@ -59,24 +65,34 @@ void createPurchaseSuccess() { var member = testHelper.createMember(mockEmail, "1234"); member.setPoint(initPoint); - var ticketing = createTicketing(member, 0, 3000, 5); + var price = 1000; + var stock = 5; + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0); + var ticket1 = ticketRepository.save(new Ticket(price, stock, stock, "ticket1", "", ticketing)); + var ticket2 = ticketRepository.save(new Ticket(price, stock, stock, "ticket2", "", ticketing)); var purchaseCount = 3; + var tickets = List.of(new CreatePurchaseCommandDto.Ticket(ticket1.getId(), purchaseCount), + new CreatePurchaseCommandDto.Ticket(ticket2.getId(), purchaseCount)); var createPurchaseCommand = CreatePurchaseCommandDto.builder() .memberEmail(mockEmail) .ticketingId(ticketing.getId()) - .count(purchaseCount) + .tickets(tickets) .build(); // when var result = createPurchaseUseCase.createPurchase(createPurchaseCommand); // then - var purchaseUnderMember = member.getPurchases().getFirst(); - Assertions.assertThat(purchaseUnderMember.getId()).isEqualTo(result.getPurchaseId()); - - var tickets = ticketRepository.findAll(); - Assertions.assertThat(tickets.size()).isEqualTo(purchaseCount); + var purchaseUnderMember = purchaseRepository.findWithTicketingByMember(member); + Assertions.assertThat(purchaseUnderMember.getFirst().getPurchaseId()).isEqualTo(result.getPurchaseId()); + Assertions.assertThat(purchaseUnderMember.getFirst().getPurchaseItems().size()).isEqualTo(purchaseCount * 2); + Assertions.assertThat(member.getPoint()).isEqualTo(initPoint - price * purchaseCount * 2); + + var ticketsInDb = ticketRepository.findAll(); + for (var ticket : ticketsInDb) { + Assertions.assertThat(ticket.getRemainingStock()).isEqualTo(stock - purchaseCount); + } } @Test @@ -86,10 +102,12 @@ void createPurchaseFailMemberNotFound() { // given var mockEmail = "test1@test.com"; + var tickets = List.of( + new CreatePurchaseCommandDto.Ticket(UUID.randomUUID(), 1)); var createPurchaseCommand = CreatePurchaseCommandDto.builder() .memberEmail(mockEmail) .ticketingId(UUID.randomUUID()) - .count(1) + .tickets(tickets) .build(); Assertions.assertThatThrownBy(() -> { @@ -106,13 +124,15 @@ void createPurchaseFailNotInSalePeriod() { // given var mockEmail = "test1@test.com"; var member = testHelper.createMember(mockEmail, "1234"); - var ticketing = createTicketing(member, 1, 1000, 1); - var ticketingInDb = ticketingRepository.findById(ticketing.getId()); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 1, 1000, 1); + var count = 1; + var tickets = List.of( + new CreatePurchaseCommandDto.Ticket(ticketRepository.findAll().getFirst().getId(), count)); var createPurchaseCommand = CreatePurchaseCommandDto.builder() .memberEmail(mockEmail) .ticketingId(ticketing.getId()) - .count(1) + .tickets(tickets) .build(); Assertions.assertThatThrownBy(() -> { @@ -129,15 +149,19 @@ void createPurchaseFailNotEnoughTicket() { // given var sellerEmail = "test1@test.com"; var seller = testHelper.createMember(sellerEmail, RoleEnum.SELLER); - var ticketing = createTicketing(seller, 0, 1000, 3); + var ticketing = ticketingTestHelper.createTicketing(seller.getId(), 0, 1000, 3); var buyerEmail = "test2@test.com"; var buyer = testHelper.createMember(buyerEmail, RoleEnum.BUYER); buyer.setPoint(10000); + + var count = 5; + var tickets = List.of( + new CreatePurchaseCommandDto.Ticket(ticketRepository.findAll().getFirst().getId(), count)); var createPurchaseCommand = CreatePurchaseCommandDto.builder() .memberEmail(buyerEmail) .ticketingId(ticketing.getId()) - .count(5) + .tickets(tickets) .build(); Assertions.assertThatThrownBy(() -> { @@ -149,20 +173,24 @@ void createPurchaseFailNotEnoughTicket() { @Test @DisplayName("구매하기 위한 금액이 부족 > 구매 요청 > 실패") + @Transactional void createPurchaseFailBecauseNotEnoughPoint() { // given var sellerEmail = "test@test.com"; var seller = testHelper.createMember(sellerEmail, RoleEnum.SELLER); - var ticketing = createTicketing(seller, 0, 3000, 5); + var ticketing = ticketingTestHelper.createTicketing(seller.getId(), 0, 3000, 5); var buyerEmail = "test2@test.com"; var buyer = testHelper.createMember(buyerEmail, RoleEnum.BUYER); buyer.setPoint(5000); + var count = 3; + var tickets = List.of( + new CreatePurchaseCommandDto.Ticket(ticketRepository.findAll().getFirst().getId(), count)); var createPurchaseCommand = CreatePurchaseCommandDto.builder() .memberEmail(buyerEmail) .ticketingId(ticketing.getId()) - .count(3) + .tickets(tickets) .build(); Assertions.assertThatThrownBy(() -> { @@ -171,25 +199,4 @@ void createPurchaseFailBecauseNotEnoughPoint() { // then }).isInstanceOf(NotEnoughPointException.class); } - - private Ticketing createTicketing(Member member, int saleStartAfterYears, long price, int stock) { - var now = LocalDateTime.now(); - var eventTime = now.plusYears(saleStartAfterYears + 2); - var saleStart = now.plusYears(saleStartAfterYears); - var saleEnd = now.plusYears(saleStartAfterYears + 1); - var ticketing = ticketingRepository.save(Ticketing.builder() - .title("test") - .member(member) - .description("") - .location("Seoul") - .eventTime(eventTime) - .saleStart(saleStart) - .saleEnd(saleEnd) - .category("concert") - .runningMinutes(300).build()); - ticketRepository.saveAll(Arrays.stream(new int[stock]) - .mapToObj(i -> Ticket.builder().ticketing(ticketing).build()) - .toList()); - return ticketing; - } } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java index 34fb886f..af86abf3 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java @@ -1,7 +1,6 @@ package com.tiketeer.Tiketeer.domain.ticketing; import java.time.LocalDateTime; -import java.util.Arrays; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; @@ -23,17 +22,13 @@ public class TicketingTestHelper { @Autowired private MemberRepository memberRepository; - public Ticketing createTicketing(UUID memberId, int saleStartAfterYears, int stock) { - return createTicketing(memberId, saleStartAfterYears, 1000, stock); - } - - public Ticketing createTicketing(UUID memberId, int saleStartAfterYears, int price, int stock) { + public Ticketing createTicketing(UUID memberId, int saleStartAfterYears) { var now = LocalDateTime.now(); var eventTime = now.plusYears(saleStartAfterYears + 2); var saleStart = now.plusYears(saleStartAfterYears); var saleEnd = now.plusYears(saleStartAfterYears + 1); var member = memberRepository.findById(memberId).orElseThrow(MemberNotFoundException::new); - var ticketing = ticketingRepository.save(Ticketing.builder() + return ticketingRepository.save(Ticketing.builder() .title("test") .member(member) .description("") @@ -43,9 +38,22 @@ public Ticketing createTicketing(UUID memberId, int saleStartAfterYears, int pri .saleEnd(saleEnd) .category("concert") .runningMinutes(300).build()); - ticketRepository.saveAll(Arrays.stream(new int[stock]) - .mapToObj(i -> Ticket.builder().ticketing(ticketing).build()) - .toList()); + } + + public Ticketing createTicketing(UUID memberId, int saleStartAfterYears, int stock) { + return createTicketing(memberId, saleStartAfterYears, 1000, stock); + } + + public Ticketing createTicketing(UUID memberId, int saleStartAfterYears, int price, int stock) { + var ticketing = createTicketing(memberId, saleStartAfterYears); + ticketRepository.save( + Ticket.builder() + .price(price) + .stock(stock) + .remainingStock(stock) + .title("ticket1") + .ticketing(ticketing) + .build()); return ticketing; } From 2cfe82d3e3c3c769faf76e60e530c7d764cf2956 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Mon, 10 Jun 2024 16:11:42 +0900 Subject: [PATCH 10/38] =?UTF-8?q?CreatePurchaseConcurrency=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ... CreatePurchaseConcurrencyTestHelper.java} | 2 +- ...UseCaseDistributedLockConcurrencyTest.java | 21 ++++++++-------- ...eUseCaseOptimisticLockConcurrencyTest.java | 21 ++++++++-------- ...UseCasePessimisticLockConcurrencyTest.java | 24 +++++++++---------- 4 files changed, 35 insertions(+), 33 deletions(-) rename src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/{CreatePurchaseConcurrencyTest.java => CreatePurchaseConcurrencyTestHelper.java} (98%) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTestHelper.java similarity index 98% rename from src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java rename to src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTestHelper.java index dc7bd6e2..44e51de9 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTestHelper.java @@ -22,7 +22,7 @@ import com.tiketeer.Tiketeer.testhelper.TestHelper; @TestComponent -public class CreatePurchaseConcurrencyTest { +public class CreatePurchaseConcurrencyTestHelper { @Autowired private CreatePurchaseUseCase createPurchaseUseCase; @Autowired diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java index 94951a70..49f45272 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java @@ -19,17 +19,18 @@ import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.usecase.decorator.CreatePurchaseUseCaseDLock; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; +import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; -import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingStockService; import com.tiketeer.Tiketeer.testhelper.RedisTestHelper; import com.tiketeer.Tiketeer.testhelper.TestHelper; import com.tiketeer.Tiketeer.testhelper.Transaction; -@Import({TestHelper.class, CreatePurchaseConcurrencyTest.class, Transaction.class, RedisTestHelper.class}) +@Import({TestHelper.class, CreatePurchaseConcurrencyTestHelper.class, Transaction.class, RedisTestHelper.class}) @SpringBootTest class CreatePurchaseUseCaseDistributedLockConcurrencyTest { @Autowired @@ -39,17 +40,17 @@ class CreatePurchaseUseCaseDistributedLockConcurrencyTest { @Autowired private TicketingRepository ticketingRepository; @Autowired - private TicketingStockService ticketingStockService; - @Autowired private MemberRepository memberRepository; @Autowired - private CreatePurchaseConcurrencyTest createPurchaseConcurrencyTest; + private CreatePurchaseConcurrencyTestHelper createPurchaseConcurrencyTestHelper; @Autowired private Transaction transaction; @Autowired private RedissonClient redissonClient; @Autowired private RedisTestHelper redisTestHelper; + @Autowired + private PurchaseItemRepository purchaseItemRepository; @BeforeEach void initTable() { @@ -73,17 +74,17 @@ void createPurchaseWithConcurrency() throws InterruptedException { int threadNums = 20; var buyers = createBuyers(threadNums); - createPurchaseConcurrencyTest.makeConcurrency(threadNums, buyers, ticketing.getId(), + createPurchaseConcurrencyTestHelper.makeConcurrency(threadNums, buyers, ticketing.getId(), ticketRepository.findAll().getFirst().getId()); //then transaction.invoke(() -> { var tickets = ticketRepository.findAll(); - assertThat(tickets.size()).isEqualTo(0); + assertThat(tickets.getFirst().getRemainingStock()).isEqualTo(0); //assert all ticket owners are unique - var purchasedTickets = ticketRepository.findAll(); - assertThat(purchasedTickets.size()).isEqualTo(ticketStock); + var purchaseItems = purchaseItemRepository.findAll(); + assertThat(purchaseItems.size()).isEqualTo(ticketStock); //assert one purchase per member var allMembers = memberRepository.findAll(); @@ -107,7 +108,7 @@ private Ticketing createTicketing(Member member, int stock) { .saleEnd(now.plusMonths(6)) .eventTime(now.plusYears(1)) .build()); - ticketingStockService.createStock(ticketing.getId(), stock); + ticketRepository.save(new Ticket(1000, stock, stock, "ticket1", "", ticketing)); return ticketing; } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java index e21606ad..7159ce82 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java @@ -18,16 +18,17 @@ import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.usecase.decorator.CreatePurchaseUseCaseOLock; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; +import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; -import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingStockService; import com.tiketeer.Tiketeer.testhelper.TestHelper; import com.tiketeer.Tiketeer.testhelper.Transaction; -@Import({TestHelper.class, CreatePurchaseConcurrencyTest.class, Transaction.class}) +@Import({TestHelper.class, CreatePurchaseConcurrencyTestHelper.class, Transaction.class}) @SpringBootTest class CreatePurchaseUseCaseOptimisticLockConcurrencyTest { @@ -38,13 +39,13 @@ class CreatePurchaseUseCaseOptimisticLockConcurrencyTest { @Autowired private TicketingRepository ticketingRepository; @Autowired - private TicketingStockService ticketingStockService; - @Autowired private MemberRepository memberRepository; @Autowired - private CreatePurchaseConcurrencyTest createPurchaseConcurrencyTest; + private CreatePurchaseConcurrencyTestHelper createPurchaseConcurrencyTestHelper; @Autowired private Transaction transaction; + @Autowired + private PurchaseItemRepository purchaseItemRepository; @BeforeEach void initTable() { @@ -67,19 +68,19 @@ void createPurchaseWithConcurrency() throws InterruptedException { int threadNums = 30; var buyers = createBuyers(threadNums); - int success = createPurchaseConcurrencyTest.makeConcurrency(threadNums, buyers, ticketing.getId(), + int success = createPurchaseConcurrencyTestHelper.makeConcurrency(threadNums, buyers, ticketing.getId(), ticketRepository.findAll().getFirst().getId()); //then transaction.invoke(() -> { var tickets = ticketRepository.findAll(); - assertThat(ticketStock - tickets.size()).isEqualTo(success); + assertThat(tickets.getFirst().getRemainingStock()).isEqualTo(0); var allMembers = memberRepository.findAll(); //assert all ticket owners are unique - var purchasedTickets = ticketRepository.findAll(); - assertThat(purchasedTickets.size()).isEqualTo(success); + var purchaseItems = purchaseItemRepository.findAll(); + assertThat(purchaseItems.size()).isEqualTo(success); //assert one purchase per member var ticketingSuccessMembers = allMembers.stream() @@ -103,7 +104,7 @@ private Ticketing createTicketing(Member member, int stock) { .saleEnd(now.plusMonths(6)) .eventTime(now.plusYears(1)) .build()); - ticketingStockService.createStock(ticketing.getId(), stock); + ticketRepository.save(new Ticket(1000, stock, stock, "ticket1", "", ticketing)); return ticketing; } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java index c0f76c54..ba957bd5 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java @@ -18,16 +18,17 @@ import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.usecase.decorator.CreatePurchaseUseCasePLock; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; +import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; -import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingStockService; import com.tiketeer.Tiketeer.testhelper.TestHelper; import com.tiketeer.Tiketeer.testhelper.Transaction; -@Import({TestHelper.class, CreatePurchaseConcurrencyTest.class, Transaction.class}) +@Import({TestHelper.class, CreatePurchaseConcurrencyTestHelper.class, Transaction.class}) @SpringBootTest class CreatePurchaseUseCasePessimisticLockConcurrencyTest { @@ -38,13 +39,13 @@ class CreatePurchaseUseCasePessimisticLockConcurrencyTest { @Autowired private TicketingRepository ticketingRepository; @Autowired - private TicketingStockService ticketingStockService; - @Autowired private MemberRepository memberRepository; @Autowired - private CreatePurchaseConcurrencyTest createPurchaseConcurrencyTest; + private CreatePurchaseConcurrencyTestHelper createPurchaseConcurrencyTestHelper; @Autowired private Transaction transaction; + @Autowired + private PurchaseItemRepository purchaseItemRepository; @BeforeEach void initTable() { @@ -67,21 +68,20 @@ void createPurchaseWithConcurrency() throws InterruptedException { int threadNums = 20; var buyers = createBuyers(threadNums); - createPurchaseConcurrencyTest.makeConcurrency(threadNums, buyers, ticketing.getId(), + createPurchaseConcurrencyTestHelper.makeConcurrency(threadNums, buyers, ticketing.getId(), ticketRepository.findAll().getFirst().getId()); //then transaction.invoke(() -> { var tickets = ticketRepository.findAll(); - assertThat(tickets.size()).isEqualTo(0); - - var allMembers = memberRepository.findAll(); + assertThat(tickets.getFirst().getRemainingStock()).isEqualTo(0); //assert all ticket owners are unique - var purchasedTickets = ticketRepository.findAll(); - assertThat(purchasedTickets.size()).isEqualTo(ticketStock); + var purchaseItems = purchaseItemRepository.findAll(); + assertThat(purchaseItems.size()).isEqualTo(ticketStock); //assert one purchase per member + var allMembers = memberRepository.findAll(); var ticketingSuccessMembers = allMembers.stream() .filter(member -> member.getPurchases().size() == 1) .toList(); @@ -103,7 +103,7 @@ private Ticketing createTicketing(Member member, int stock) { .saleEnd(now.plusMonths(6)) .eventTime(now.plusYears(1)) .build()); - ticketingStockService.createStock(ticketing.getId(), stock); + ticketRepository.save(new Ticket(1000, stock, stock, "ticket1", "", ticketing)); return ticketing; } From 4f9243303112afa9b128ffdea26ac96f450b1362 Mon Sep 17 00:00:00 2001 From: punkryn Date: Mon, 10 Jun 2024 16:21:40 +0900 Subject: [PATCH 11/38] =?UTF-8?q?ticketing=20repository=20=EB=A7=88?= =?UTF-8?q?=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/GetMemberTicketingSalesResultDto.java | 28 ++++---- .../Tiketeer/domain/ticket/Ticket.java | 17 ++++- .../Tiketeer/domain/ticketing/Ticketing.java | 9 ++- .../dto/GetTicketingResponseDto.java | 19 ++---- .../repository/TicketingRepository.java | 30 ++++----- .../usecase/GetTicketingUseCase.java | 5 +- .../ticketing/usecase/dto/GetTicketing.java | 67 +++++++++++++++++++ .../usecase/dto/GetTicketingResultDto.java | 53 ++++++++++++--- .../controller/MemberControllerTest.java | 14 ++-- .../service/MemberTicketingServiceTest.java | 6 +- .../GetMemberPurchasesUseCaseTest.java | 8 +-- .../repository/PurchaseRepositoryTest.java | 8 +-- .../controller/TicketingControllerTest.java | 4 +- .../repository/TicketingRepositoryTest.java | 41 +++++++----- .../usecase/GetAllTicketingsUseCaseTest.java | 2 +- .../usecase/GetTicketingUseCaseTest.java | 4 +- 16 files changed, 215 insertions(+), 100 deletions(-) create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/GetTicketing.java diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberTicketingSalesResultDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberTicketingSalesResultDto.java index 1a40ad3e..c0d29871 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberTicketingSalesResultDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberTicketingSalesResultDto.java @@ -12,6 +12,19 @@ @ToString @NoArgsConstructor(force = true) public class GetMemberTicketingSalesResultDto { + private final UUID ticketingId; + private final String title; + private final String description; + private final String location; + private final LocalDateTime eventTime; + private final LocalDateTime saleStart; + private final LocalDateTime saleEnd; + private final long stock; + private final long remainStock; + private final LocalDateTime createdAt; + private final String category; + private final int runningMinutes; + private final long price; @Builder public GetMemberTicketingSalesResultDto( @@ -43,19 +56,4 @@ public GetMemberTicketingSalesResultDto( this.runningMinutes = runningMinutes; this.price = price; } - - private final UUID ticketingId; - private final String title; - private final String description; - private final String location; - private final LocalDateTime eventTime; - private final LocalDateTime saleStart; - private final LocalDateTime saleEnd; - private final long stock; - private final long remainStock; - private final LocalDateTime createdAt; - private final String category; - private final int runningMinutes; - private final long price; - } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java index a25d9b38..b2c52052 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java @@ -73,11 +73,24 @@ public class Ticket { private Integer version; @Builder - public Ticket(long price, int stock, int remainingStock, Ticketing ticketing) { + public Ticket(long price, int stock, int remainingStock, String title, Ticketing ticketing) { this.price = price; this.stock = stock; this.remainingStock = remainingStock; - this.ticketing = ticketing; + this.title = title; + setTicketing(ticketing); } + public void setTicketing(Ticketing ticketing) { + if (this.ticketing != null) { + this.ticketing.getTickets().remove(this); + } + this.ticketing = ticketing; + + if (ticketing == null) { + return; + } + + this.ticketing.getTickets().add(this); + } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/Ticketing.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/Ticketing.java index c42ce907..22fdc74d 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/Ticketing.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/Ticketing.java @@ -1,6 +1,8 @@ package com.tiketeer.Tiketeer.domain.ticketing; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import org.hibernate.annotations.UuidGenerator; @@ -9,6 +11,7 @@ import com.tiketeer.Tiketeer.constant.StorageEnum; import com.tiketeer.Tiketeer.domain.member.Member; +import com.tiketeer.Tiketeer.domain.ticket.Ticket; import jakarta.persistence.Column; import jakarta.persistence.ConstraintMode; @@ -21,6 +24,7 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; @@ -29,14 +33,12 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import lombok.ToString; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Table(name = "ticketings") @EntityListeners(AuditingEntityListener.class) @Getter -@ToString public class Ticketing { @Id @UuidGenerator @@ -98,6 +100,9 @@ public class Ticketing { @Column(name = "created_at", nullable = false) private LocalDateTime createdAt; + @OneToMany(mappedBy = "ticketing") + private List tickets = new ArrayList<>(); + @Builder public Ticketing( Member member, diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/GetTicketingResponseDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/GetTicketingResponseDto.java index cb4a6b10..86e83206 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/GetTicketingResponseDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/GetTicketingResponseDto.java @@ -1,6 +1,7 @@ package com.tiketeer.Tiketeer.domain.ticketing.controller.dto; import java.time.LocalDateTime; +import java.util.List; import java.util.UUID; import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.GetTicketingResultDto; @@ -18,15 +19,13 @@ public class GetTicketingResponseDto { private final String location; private final String category; private final Integer runningMinutes; - private final Long stock; - private final Long remainStock; - private final Long price; private final LocalDateTime eventTime; private final LocalDateTime saleStart; private final LocalDateTime saleEnd; private final LocalDateTime createdAt; private final String owner; private final String thumbnailPath; + private final List tickets; @Builder public GetTicketingResponseDto(UUID ticketingId, String title, @@ -34,28 +33,24 @@ public GetTicketingResponseDto(UUID ticketingId, String title, String location, String category, Integer runningMinutes, - Long stock, - Long remainStock, - Long price, LocalDateTime eventTime, LocalDateTime saleStart, LocalDateTime saleEnd, LocalDateTime createdAt, String owner, - String thumbnailPath) { + String thumbnailPath, + List tickets) { this.ticketingId = ticketingId; this.title = title; this.description = description; this.location = location; this.category = category; this.runningMinutes = runningMinutes; - this.stock = stock; - this.remainStock = remainStock; - this.price = price; this.eventTime = eventTime; this.saleStart = saleStart; this.saleEnd = saleEnd; this.createdAt = createdAt; this.owner = owner; this.thumbnailPath = thumbnailPath; + this.tickets = tickets; } public static GetTicketingResponseDto convertFromDto(GetTicketingResultDto dto) { @@ -66,15 +61,13 @@ public static GetTicketingResponseDto convertFromDto(GetTicketingResultDto dto) .location(dto.getLocation()) .category(dto.getCategory()) .runningMinutes(dto.getRunningMinutes()) - .stock(dto.getStock()) - .remainStock(dto.getRemainStock()) - .price(dto.getPrice()) .eventTime(dto.getEventTime()) .saleStart(dto.getSaleStart()) .saleEnd(dto.getSaleEnd()) .createdAt(dto.getCreatedAt()) .owner(dto.getOwner()) .thumbnailPath(dto.getThumbnailPath()) + .tickets(dto.getTickets()) .build(); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepository.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepository.java index 788fe83f..d0eb35df 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepository.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepository.java @@ -12,7 +12,7 @@ import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberTicketingSalesResultDto; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.GetAllTicketingsResultDto; -import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.GetTicketingResultDto; +import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.GetTicketing; @Repository public interface TicketingRepository extends JpaRepository { @@ -20,9 +20,10 @@ public interface TicketingRepository extends JpaRepository { @Query(value = """ select new com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberTicketingSalesResultDto( t.id, t.title, t.description, t.location, t.eventTime, t.saleStart, t.saleEnd, - (select count(tk) from Ticket tk where tk.ticketing = t), - (select count(tk) from Ticket tk where tk.ticketing = t), - t.createdAt, t.category, t.runningMinutes, 0 as price + (select SUM(tk.stock) from Ticket tk where tk.ticketing = t), + (select SUM(tk.remainingStock) from Ticket tk where tk.ticketing = t), + t.createdAt, t.category, t.runningMinutes, + (select SUM(tk.price) from Ticket tk where tk.ticketing = t) ) from Ticketing t where t.member.email = :email @@ -32,26 +33,21 @@ public interface TicketingRepository extends JpaRepository { @Query(value = """ SELECT new com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.GetAllTicketingsResultDto( - t.id, t.title, t.location, t.category, t.runningMinutes, COUNT(*), 0 as price, t.eventTime, + t.id, t.title, t.location, t.category, t.runningMinutes, SUM(tk.remainingStock), MIN(tk.price), t.eventTime, t.saleStart, t.saleEnd, t.createdAt - ) - FROM Ticketing t - LEFT JOIN Ticket tk ON t = tk.ticketing + ) + FROM Ticketing t + LEFT JOIN Ticket tk ON t = tk.ticketing GROUP BY t.id """) List findAllTicketingsWithRemainStock(); @Query(value = """ - SELECT new com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.GetTicketingResultDto( - t.id, t.title, t.description, t.location, t.category, t.runningMinutes, - count(*), count(*), - 0 as price, t.eventTime, - t.saleStart, t.saleEnd, t.createdAt, t.member.email, t.thumbnailPath - ) - FROM Ticketing t LEFT JOIN Ticket tk ON t = tk.ticketing - WHERE t.id = :ticketingId GROUP BY t.id + SELECT t + FROM Ticketing t LEFT JOIN FETCH t.tickets tk + WHERE t.id = :ticketingId """) - GetTicketingResultDto findTicketingWithRemainStock(@Param("ticketingId") UUID ticketingId); + GetTicketing findTicketingWithRemainStock(@Param("ticketingId") UUID ticketingId); List findAllByMember(Member member); } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCase.java index cbe77f34..f7de1f79 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCase.java @@ -6,6 +6,7 @@ import com.tiketeer.Tiketeer.domain.ticket.service.TicketCrudService; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; +import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.GetTicketing; import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.GetTicketingCommandDto; import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.GetTicketingResultDto; @@ -22,6 +23,8 @@ public GetTicketingUseCase(TicketingRepository ticketingRepository, TicketCrudSe } public GetTicketingResultDto getTicketing(GetTicketingCommandDto command) { - return ticketingRepository.findTicketingWithRemainStock(command.getTicketingId()); + GetTicketing ticketingWithRemainStock = ticketingRepository.findTicketingWithRemainStock( + command.getTicketingId()); + return new GetTicketingResultDto(ticketingWithRemainStock); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/GetTicketing.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/GetTicketing.java new file mode 100644 index 00000000..67ca7ea0 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/GetTicketing.java @@ -0,0 +1,67 @@ +package com.tiketeer.Tiketeer.domain.ticketing.usecase.dto; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Value; + +public interface GetTicketing { + @Value("#{target.id}") + UUID getTicketingId(); + + @Value("#{target.title}") + String getTitle(); + + @Value("#{target.description}") + String getDescription(); + + @Value("#{target.location}") + String getLocation(); + + @Value("#{target.category}") + String getCategory(); + + @Value("#{target.runningMinutes}") + Integer getRunningMinutes(); + + @Value("#{target.eventTime}") + LocalDateTime getEventTime(); + + @Value("#{target.saleStart}") + LocalDateTime getSaleStart(); + + @Value("#{target.saleEnd}") + LocalDateTime getSaleEnd(); + + @Value("#{target.createdAt}") + LocalDateTime getCreatedAt(); + + @Value("#{target.owner}") + String getOwner(); + + @Value("#{target.thumbnailPath}") + String getThumbnailPath(); + + List getTickets(); + + interface GetTicketingTickets { + @Value("#{target.id}") + UUID getTicketId(); + + @Value("#{target.price}") + Long getPrice(); + + @Value("#{target.stock}") + Integer getStock(); + + @Value("#{target.remainingStock}") + Integer getRemainingStock(); + + @Value("#{target.title}") + String getTitle(); + + @Value("#{target.description}") + String getDescription(); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/GetTicketingResultDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/GetTicketingResultDto.java index 4fea9673..6ccbbe5d 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/GetTicketingResultDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/GetTicketingResultDto.java @@ -1,10 +1,12 @@ package com.tiketeer.Tiketeer.domain.ticketing.usecase.dto; import java.time.LocalDateTime; +import java.util.List; import java.util.UUID; import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.ToString; @ToString @@ -16,15 +18,13 @@ public class GetTicketingResultDto { private final String location; private final String category; private final Integer runningMinutes; - private final Long stock; - private final Long remainStock; - private final Long price; private final LocalDateTime eventTime; private final LocalDateTime saleStart; private final LocalDateTime saleEnd; private final LocalDateTime createdAt; private final String owner; private final String thumbnailPath; + private final List tickets; @Builder public GetTicketingResultDto(UUID ticketingId, String title, @@ -32,27 +32,60 @@ public GetTicketingResultDto(UUID ticketingId, String title, String location, String category, Integer runningMinutes, - Long stock, - Long remainStock, - Long price, LocalDateTime eventTime, LocalDateTime saleStart, LocalDateTime saleEnd, LocalDateTime createdAt, String owner, - String thumbnailPath) { + String thumbnailPath, + List tickets) { this.ticketingId = ticketingId; this.title = title; this.description = description; this.location = location; this.category = category; this.runningMinutes = runningMinutes; - this.stock = stock; - this.remainStock = remainStock; - this.price = price; this.eventTime = eventTime; this.saleStart = saleStart; this.saleEnd = saleEnd; this.createdAt = createdAt; this.owner = owner; this.thumbnailPath = thumbnailPath; + this.tickets = tickets; + } + + public GetTicketingResultDto(GetTicketing t) { + this.ticketingId = t.getTicketingId(); + this.title = t.getTitle(); + this.description = t.getDescription(); + this.location = t.getLocation(); + this.category = t.getCategory(); + this.runningMinutes = t.getRunningMinutes(); + this.eventTime = t.getEventTime(); + this.saleStart = t.getSaleStart(); + this.saleEnd = t.getSaleEnd(); + this.createdAt = t.getCreatedAt(); + this.owner = t.getOwner(); + this.thumbnailPath = t.getThumbnailPath(); + this.tickets = t.getTickets().stream().map(TicketingTicket::new).toList(); + } + + @Getter + @ToString + @NoArgsConstructor(force = true) + public static class TicketingTicket { + private final UUID id; + private long price; + private final Integer stock; + private final Integer remainingStock; + private final String title; + private final String description; + + public TicketingTicket(GetTicketing.GetTicketingTickets gtt) { + this.id = gtt.getTicketId(); + this.price = gtt.getPrice(); + this.stock = gtt.getStock(); + this.remainingStock = gtt.getRemainingStock(); + this.title = gtt.getTitle(); + this.description = gtt.getDescription(); + } } } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java index 8ad5aa52..b543de93 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java @@ -99,9 +99,9 @@ void getMemberTicketingSalesSuccess() throws Exception { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); //when - then MvcResult result = mockMvc.perform(get("/api/members/" + member.getId() + "/sale") @@ -364,10 +364,10 @@ void getMemberPurchasesSuccess() throws Exception { ); var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing2)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing2)); // when var result = mockMvc.perform(get("/api/members/{memberId}/purchases", member.getId()) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java index 7dc906c0..07a31874 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java @@ -69,9 +69,9 @@ void getMemberTicketingSalesSuccess() { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); //when var memberTicketingSale = getMemberTicketingSalesUseCase.getMemberTicketingSales( diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java index 570541b3..55c5a655 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java @@ -84,10 +84,10 @@ void getMemberPurchases() { ); var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing2)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing2)); // when var results = getMemberPurchasesUseCase.getMemberPurchases( diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java index 8f466e42..c7046f85 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java @@ -74,10 +74,10 @@ void findWithTicketingByMember() { ); var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing2)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 2", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 3", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 4", ticketing2)); // when var results = purchaseRepository.findWithTicketingByMember(member); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java index 2ead3a2d..5ada3655 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java @@ -162,8 +162,8 @@ void getTicketingSuccess() throws Exception { result.get().andReturn().getResponse().getContentAsString(), GetTicketingResponseDto.class); var ticketing = apiResponse.getData(); Assertions.assertThat(ticketing.getTitle()).isEqualTo("0"); - Assertions.assertThat(ticketing.getStock()).isEqualTo(stock); - Assertions.assertThat(ticketing.getRemainStock()).isEqualTo(stock); + // Assertions.assertThat(ticketing.getStock()).isEqualTo(stock); + // Assertions.assertThat(ticketing.getRemainStock()).isEqualTo(stock); Assertions.assertThat(ticketing.getOwner()).isEqualTo(member.getEmail()); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java index c5188605..99c150bb 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java @@ -19,6 +19,7 @@ import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; +import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.GetTicketing; import com.tiketeer.Tiketeer.testhelper.TestHelper; @Import({TestHelper.class}) @@ -64,9 +65,9 @@ void findTicketingWithTicketStock() { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); + ticketRepository.save(new Ticket(2000, 2, 2, "title 2", ticketing)); + ticketRepository.save(new Ticket(3000, 3, 3, "title 3", ticketing)); //when var memberTicketingSale = ticketingRepository.findTicketingWithTicketStock(member.getEmail()).getFirst(); @@ -78,8 +79,9 @@ void findTicketingWithTicketStock() { assertThat(memberTicketingSale.getSaleStart()).isEqualTo(now); assertThat(memberTicketingSale.getSaleEnd()).isEqualTo(now); - assertThat(memberTicketingSale.getRemainStock()).isEqualTo(1); - assertThat(memberTicketingSale.getStock()).isEqualTo(3); + assertThat(memberTicketingSale.getRemainStock()).isEqualTo(6); + assertThat(memberTicketingSale.getStock()).isEqualTo(6); + assertThat(memberTicketingSale.getPrice()).isEqualTo(6000); } @@ -104,7 +106,7 @@ void findAllTicketingsWithRemainStockSuccess() { ); var stock1 = 3; for (int i = 0; i < stock1; i++) { - ticketRepository.save(new Ticket(1000, 1, 1, ticketing1)); + ticketRepository.save(new Ticket(1000 * (i + 1), i + 1, i + 1, "title " + i, ticketing1)); } var ticketing2 = ticketingRepository.save( Ticketing.builder() @@ -121,7 +123,7 @@ void findAllTicketingsWithRemainStockSuccess() { ); var stock2 = 1; for (int i = 0; i < stock2; i++) { - ticketRepository.save(new Ticket(1000, 1, 1, ticketing2)); + ticketRepository.save(new Ticket(1000 * (i + 1), i + 1, i + 1, "title " + i, ticketing2)); } //when @@ -138,16 +140,20 @@ void findAllTicketingsWithRemainStockSuccess() { .findFirst(); assertThat(ticketing1Opt).isPresent(); assertThat(ticketing2Opt).isPresent(); + assertThat(ticketing1Opt.get().getLocation()).isEqualTo("Seoul"); assertThat(ticketing1Opt.get().getRunningMinutes()).isEqualTo(600); assertThat(ticketing1Opt.get().getSaleStart()).isEqualTo(now); assertThat(ticketing1Opt.get().getSaleEnd()).isEqualTo(now); - assertThat(ticketing1Opt.get().getRemainStock()).isEqualTo(stock1); + assertThat(ticketing1Opt.get().getRemainStock()).isEqualTo((stock1 * (stock1 + 1)) / 2); + assertThat(ticketing1Opt.get().getPrice()).isEqualTo(1000); + assertThat(ticketing2Opt.get().getLocation()).isEqualTo("Seoul"); assertThat(ticketing2Opt.get().getRunningMinutes()).isEqualTo(600); assertThat(ticketing2Opt.get().getSaleStart()).isEqualTo(now); assertThat(ticketing2Opt.get().getSaleEnd()).isEqualTo(now); - assertThat(ticketing2Opt.get().getRemainStock()).isEqualTo(stock2); + assertThat(ticketing2Opt.get().getRemainStock()).isEqualTo((stock2 + (stock2 + 1)) / 2); + assertThat(ticketing2Opt.get().getPrice()).isEqualTo(1000); } @Test @@ -171,12 +177,8 @@ void findTicketingWithRemainStock() { ); var purchase = purchaseRepository.save(new Purchase(member)); var stock = 3; - var remainStock = 1; - for (int i = 0; i < stock - remainStock; i++) { - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); - } - for (int i = 0; i < remainStock; i++) { - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + for (int i = 0; i < stock; i++) { + ticketRepository.save(new Ticket(1000 * (i + 1), i + 1, i, "title " + i, ticketing)); } //when @@ -189,7 +191,12 @@ void findTicketingWithRemainStock() { assertThat(result.getDescription()).isEqualTo(""); assertThat(result.getSaleStart()).isEqualTo(now); assertThat(result.getSaleEnd()).isEqualTo(now); - assertThat(result.getRemainStock()).isEqualTo(remainStock); - assertThat(result.getStock()).isEqualTo(stock); + + assertThat(result.getTickets().size()).isEqualTo(stock); + GetTicketing.GetTicketingTickets ticket = result.getTickets().getFirst(); + assertThat(ticket.getTitle()).isEqualTo("title 0"); + + assertThat(ticket.getRemainingStock()).isEqualTo(0); + assertThat(ticket.getStock()).isEqualTo(1); } } \ No newline at end of file diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java index bd1d557c..f1921938 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java @@ -91,7 +91,7 @@ private List createTicketings(Member member, int count) { .build() ).toList(); ticketings.forEach(ticketing -> { - ticketRepository.save(new Ticket(1000, 1, 1, ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); }); return ticketingRepository.saveAll(ticketings); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCaseTest.java index 76ce358e..cc36ece3 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCaseTest.java @@ -74,8 +74,8 @@ void getTicketingSuccess() { // then Assertions.assertThat(result.getTitle()).isEqualTo("0"); - Assertions.assertThat(result.getStock()).isEqualTo(stock); - Assertions.assertThat(result.getRemainStock()).isEqualTo(stock - purchasedStock); + // Assertions.assertThat(result.getStock()).isEqualTo(stock); + // Assertions.assertThat(result.getRemainStock()).isEqualTo(stock - purchasedStock); Assertions.assertThat(result.getOwner()).isEqualTo(member.getEmail()); } From f0dc242f8680ff3a24f9674a6ba060e4752f719e Mon Sep 17 00:00:00 2001 From: dla0510 Date: Mon, 10 Jun 2024 16:31:21 +0900 Subject: [PATCH 12/38] =?UTF-8?q?findByIdIn=20->=20findAllById=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/purchase/usecase/CreatePurchaseUseCase.java | 2 +- .../domain/purchase/usecase/CreatePurchaseUseCaseCore.java | 4 ++-- .../usecase/decorator/CreatePurchaseUseCaseOLock.java | 2 +- .../usecase/decorator/CreatePurchaseUseCasePLock.java | 2 +- .../Tiketeer/domain/ticket/repository/TicketRepository.java | 6 ++---- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCase.java index 2d448b49..868ace17 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCase.java @@ -17,6 +17,6 @@ default CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command, @FunctionalInterface interface ListTicketStrategy { - List findByIdIn(List ticketIds); + List findAllById(List ticketIds); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java index 087f0a7f..4830c7fb 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java @@ -55,7 +55,7 @@ public CreatePurchaseUseCaseCore( @Override @Transactional public CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command) { - return execPurchaseLogic(command, ticketRepository::findByIdIn); + return execPurchaseLogic(command, ticketRepository::findAllById); } @Override @@ -76,7 +76,7 @@ private CreatePurchaseResultDto execPurchaseLogic(CreatePurchaseCommandDto comma var purchase = purchaseRepository.save(Purchase.builder().member(member).build()); var ticketIds = command.getTickets().stream().map(CreatePurchaseCommandDto.Ticket::getId).toList(); - var tickets = listTicketStrategy.findByIdIn(ticketIds); + var tickets = listTicketStrategy.findAllById(ticketIds); Map ticketMap = new HashMap<>(tickets.size()); for (var t : tickets) { ticketMap.put(t.getId(), t); diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCaseOLock.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCaseOLock.java index d6bd2e2c..56cc4280 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCaseOLock.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCaseOLock.java @@ -31,6 +31,6 @@ public CreatePurchaseUseCaseOLock(CreatePurchaseUseCase createPurchaseUseCase, ) public CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command) { return innerUseCase.createPurchase(command, - ticketRepository::findByIdInWithOptimisticLock); + ticketRepository::findAllByIdWithOptimisticLock); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCasePLock.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCasePLock.java index 4a257f3c..8888145c 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCasePLock.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/decorator/CreatePurchaseUseCasePLock.java @@ -17,6 +17,6 @@ public CreatePurchaseUseCasePLock(CreatePurchaseUseCase createPurchaseUseCase, T @Override public CreatePurchaseResultDto createPurchase(CreatePurchaseCommandDto command) { return innerUseCase.createPurchase(command, - ticketRepository::findByIdInWithPessimisticLock); + ticketRepository::findAllByIdWithPessimisticLock); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java index 3a89d058..4142305f 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java @@ -20,8 +20,6 @@ public interface TicketRepository extends JpaRepository { List findByTicketingIdOrderById(UUID ticketingId, Limit limit); - List findByIdIn(List ticketIds); - @Lock(LockModeType.PESSIMISTIC_WRITE) @Query("SELECT t FROM Ticket t WHERE t.ticketing.id = :ticketingId ORDER BY t.id") List findByTicketingIdOrderByIdWithPessimisticLock(UUID ticketingId, Limit limit); @@ -32,9 +30,9 @@ public interface TicketRepository extends JpaRepository { @Lock(LockModeType.PESSIMISTIC_WRITE) @Query("SELECT t FROM Ticket t WHERE t.id IN :ticketIds") - List findByIdInWithPessimisticLock(List ticketIds); + List findAllByIdWithPessimisticLock(List ticketIds); @Lock(LockModeType.OPTIMISTIC) @Query("SELECT t FROM Ticket t WHERE t.id IN :ticketIds") - List findByIdInWithOptimisticLock(List ticketIds); + List findAllByIdWithOptimisticLock(List ticketIds); } From e10b3ee3b5c4e4a51b7941129add3918630a7313 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Tue, 11 Jun 2024 02:08:38 +0900 Subject: [PATCH 13/38] =?UTF-8?q?Optimistic=20Lock,=20Pessimistic=20Lock?= =?UTF-8?q?=20=EB=8F=99=EC=8B=9C=20=EC=A0=91=EA=B7=BC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ticket/service/TicketCrudService.java | 55 +++++++++++++++++++ .../ticket/service/TicketCrudServiceTest.java | 49 ++++++++++++++++- .../domain/ticketing/TicketingTestHelper.java | 7 +++ .../Tiketeer/testhelper/TestHelper.java | 7 +++ 4 files changed, 117 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java index 91100696..4eb7737a 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java @@ -5,8 +5,13 @@ import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.OptimisticLockingFailureException; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; @@ -14,7 +19,10 @@ import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; +import lombok.extern.slf4j.Slf4j; + @Service +@Slf4j @Transactional(readOnly = true) public class TicketCrudService { private final TicketRepository ticketRepository; @@ -45,6 +53,53 @@ public void deleteAllByTicketIds(List ticketIds) { ticketRepository.deleteAllByIdInBatch(ticketIds); } + @Transactional + @Retryable(retryFor = OptimisticLockingFailureException.class, + backoff = @Backoff( + delayExpression = "#{T(java.util.concurrent.ThreadLocalRandom).current().nextInt(300, 501)}", + maxDelay = 500, + multiplier = 1 + ), + maxAttempts = 10) + public void updateTicketRemainingStockWithOLock(List ticketIds, int change) throws InterruptedException { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + @Override + public void afterCompletion(int status) { + //do what you want to do after commit + log.debug("updateTicketRemainingStockWithOLock is done with " + (status == 0 ? "commit" : "rollback")); + } + }); + var tickets = ticketRepository.findAllByIdWithOptimisticLock(ticketIds); + log.debug( + "Successfully get ticket with OptimisticLock : " + tickets.getFirst().getId() + "(remainingStock: " + + tickets.getFirst().getRemainingStock() + ")"); + Thread.sleep(1000); + tickets.forEach(t -> t.setRemainingStock(t.getRemainingStock() + change)); + log.debug( + "Successfully update ticket stock with OptimisticLock : " + tickets.getFirst().getId() + "(remainingStock: " + + tickets.getFirst().getRemainingStock() + ")"); + } + + @Transactional + public void updateTicketRemainingStockWithPLock(List ticketIds, int change) throws InterruptedException { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + @Override + public void afterCompletion(int status) { + //do what you want to do after commit + log.debug("updateTicketRemainingStockWithPLock is done with " + (status == 0 ? "commit" : "rollback")); + } + }); + + var tickets = ticketRepository.findAllByIdWithPessimisticLock(ticketIds); + log.debug( + "Successfully get ticket with PessimisticLock : " + tickets.getFirst().getId() + "(remainingStock: " + + tickets.getFirst().getRemainingStock() + ")"); + Thread.sleep(1000); + tickets.forEach(t -> t.setRemainingStock(t.getRemainingStock() + change)); + log.debug("Successfully update ticket stock with PessimisticLock : " + tickets.getFirst().getId() + + "(remainingStock: " + tickets.getFirst().getRemainingStock() + ")"); + } + private Ticketing findTicketingById(UUID ticketingId) { return ticketingRepository.findById(ticketingId).orElseThrow(TicketingNotFoundException::new); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java index a4f31b61..b1ea5338 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java @@ -2,7 +2,11 @@ import java.time.LocalDateTime; import java.util.Arrays; +import java.util.List; import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; @@ -12,6 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; +import org.springframework.dao.ConcurrencyFailureException; import org.springframework.transaction.annotation.Transactional; import com.tiketeer.Tiketeer.domain.member.Member; @@ -20,18 +25,21 @@ import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; +import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingService; import com.tiketeer.Tiketeer.testhelper.TestHelper; -@Import({TestHelper.class}) +@Import({TestHelper.class, TicketingTestHelper.class}) @SpringBootTest @DisplayName("TicketService 테스트") public class TicketCrudServiceTest { @Autowired private TestHelper testHelper; @Autowired + private TicketingTestHelper ticketingTestHelper; + @Autowired private TicketCrudService ticketCrudService; @Autowired private TicketingService ticketingService; @@ -141,4 +149,43 @@ private UUID createTicketingAndReturnId(Member member, int stock, LocalDateTime .toList()); return ticketing.getId(); } + + @Test + @DisplayName("OLock 접근 > PLock 접근 > OLock 레코드 업데이트 > PLock 레코드 업데이트") + void testWithOptimisticLociAndPessimisticLock() throws InterruptedException { + var memberId = testHelper.createMemberAndReturnId("test@email.com"); + var ticketingId = ticketingTestHelper.createTicketingAndReturnId(memberId, 1, 5); + var ticketId = ticketRepository.findAll().getFirst().getId(); + + var threadNums = 2; + ExecutorService executorService = Executors.newFixedThreadPool(threadNums); + CountDownLatch endLatch = new CountDownLatch(threadNums); + + // OptimisticLock + executorService.submit(() -> { + try { + ticketCrudService.updateTicketRemainingStockWithOLock(List.of(ticketId), -1); + } catch (Exception e) { + throw new ConcurrencyFailureException("Failed to update ticket"); + } finally { + endLatch.countDown(); + } + }); + Thread.sleep(200); // 0.2초 대기 + // PessimisticLock + executorService.submit(() -> { + try { + ticketCrudService.updateTicketRemainingStockWithPLock(List.of(ticketId), 1); + } catch (Exception e) { + throw new ConcurrencyFailureException("Failed to update ticket"); + } finally { + endLatch.countDown(); + } + }); + endLatch.await(); + + var ticket = ticketRepository.findAll().getFirst(); + System.out.println( + "Ticket remainingStock : " + ticket.getRemainingStock()); + } } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java index af86abf3..0e72b4db 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java @@ -5,6 +5,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.TestComponent; +import org.springframework.transaction.annotation.Transactional; import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; @@ -22,6 +23,12 @@ public class TicketingTestHelper { @Autowired private MemberRepository memberRepository; + @Transactional + public UUID createTicketingAndReturnId(UUID memberId, int saleStartAfterYears, int stock) { + var ticketing = createTicketing(memberId, saleStartAfterYears, stock); + return ticketing.getId(); + } + public Ticketing createTicketing(UUID memberId, int saleStartAfterYears) { var now = LocalDateTime.now(); var eventTime = now.plusYears(saleStartAfterYears + 2); diff --git a/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelper.java b/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelper.java index 9708a355..74a2a97e 100644 --- a/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelper.java +++ b/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelper.java @@ -2,6 +2,7 @@ import java.time.LocalDateTime; import java.util.List; +import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.TestComponent; @@ -167,6 +168,12 @@ public Member createMember(String email, String password, RoleEnum roleEnum, lon .build()); } + @Transactional + public UUID createMemberAndReturnId(String email) { + var member = createMember(email); + return member.getId(); + } + public ApiResponse> getDeserializedListApiResponse(String json, Class responseType) throws JsonProcessingException { return objectMapper.readValue(json, getListApiResponseType(responseType)); From db4ce424af79a85ae85d5fc734d09f755e118e86 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Tue, 11 Jun 2024 02:09:57 +0900 Subject: [PATCH 14/38] =?UTF-8?q?Optimistic=20Lock,=20Pessimistic=20Lock?= =?UTF-8?q?=20=EB=8F=99=EC=8B=9C=20=EC=A0=91=EA=B7=BC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ticket/service/TicketCrudService.java | 52 ------------------- .../ticket/service/TicketCrudServiceTest.java | 44 ---------------- .../domain/ticketing/TicketingTestHelper.java | 7 --- .../Tiketeer/testhelper/TestHelper.java | 7 --- 4 files changed, 110 deletions(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java index 4eb7737a..0ed18f49 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java @@ -5,13 +5,8 @@ import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.OptimisticLockingFailureException; -import org.springframework.retry.annotation.Backoff; -import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionSynchronization; -import org.springframework.transaction.support.TransactionSynchronizationManager; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; @@ -53,53 +48,6 @@ public void deleteAllByTicketIds(List ticketIds) { ticketRepository.deleteAllByIdInBatch(ticketIds); } - @Transactional - @Retryable(retryFor = OptimisticLockingFailureException.class, - backoff = @Backoff( - delayExpression = "#{T(java.util.concurrent.ThreadLocalRandom).current().nextInt(300, 501)}", - maxDelay = 500, - multiplier = 1 - ), - maxAttempts = 10) - public void updateTicketRemainingStockWithOLock(List ticketIds, int change) throws InterruptedException { - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - @Override - public void afterCompletion(int status) { - //do what you want to do after commit - log.debug("updateTicketRemainingStockWithOLock is done with " + (status == 0 ? "commit" : "rollback")); - } - }); - var tickets = ticketRepository.findAllByIdWithOptimisticLock(ticketIds); - log.debug( - "Successfully get ticket with OptimisticLock : " + tickets.getFirst().getId() + "(remainingStock: " - + tickets.getFirst().getRemainingStock() + ")"); - Thread.sleep(1000); - tickets.forEach(t -> t.setRemainingStock(t.getRemainingStock() + change)); - log.debug( - "Successfully update ticket stock with OptimisticLock : " + tickets.getFirst().getId() + "(remainingStock: " - + tickets.getFirst().getRemainingStock() + ")"); - } - - @Transactional - public void updateTicketRemainingStockWithPLock(List ticketIds, int change) throws InterruptedException { - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - @Override - public void afterCompletion(int status) { - //do what you want to do after commit - log.debug("updateTicketRemainingStockWithPLock is done with " + (status == 0 ? "commit" : "rollback")); - } - }); - - var tickets = ticketRepository.findAllByIdWithPessimisticLock(ticketIds); - log.debug( - "Successfully get ticket with PessimisticLock : " + tickets.getFirst().getId() + "(remainingStock: " - + tickets.getFirst().getRemainingStock() + ")"); - Thread.sleep(1000); - tickets.forEach(t -> t.setRemainingStock(t.getRemainingStock() + change)); - log.debug("Successfully update ticket stock with PessimisticLock : " + tickets.getFirst().getId() - + "(remainingStock: " + tickets.getFirst().getRemainingStock() + ")"); - } - private Ticketing findTicketingById(UUID ticketingId) { return ticketingRepository.findById(ticketingId).orElseThrow(TicketingNotFoundException::new); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java index b1ea5338..faa1a807 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java @@ -2,11 +2,7 @@ import java.time.LocalDateTime; import java.util.Arrays; -import java.util.List; import java.util.UUID; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; @@ -16,7 +12,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; -import org.springframework.dao.ConcurrencyFailureException; import org.springframework.transaction.annotation.Transactional; import com.tiketeer.Tiketeer.domain.member.Member; @@ -149,43 +144,4 @@ private UUID createTicketingAndReturnId(Member member, int stock, LocalDateTime .toList()); return ticketing.getId(); } - - @Test - @DisplayName("OLock 접근 > PLock 접근 > OLock 레코드 업데이트 > PLock 레코드 업데이트") - void testWithOptimisticLociAndPessimisticLock() throws InterruptedException { - var memberId = testHelper.createMemberAndReturnId("test@email.com"); - var ticketingId = ticketingTestHelper.createTicketingAndReturnId(memberId, 1, 5); - var ticketId = ticketRepository.findAll().getFirst().getId(); - - var threadNums = 2; - ExecutorService executorService = Executors.newFixedThreadPool(threadNums); - CountDownLatch endLatch = new CountDownLatch(threadNums); - - // OptimisticLock - executorService.submit(() -> { - try { - ticketCrudService.updateTicketRemainingStockWithOLock(List.of(ticketId), -1); - } catch (Exception e) { - throw new ConcurrencyFailureException("Failed to update ticket"); - } finally { - endLatch.countDown(); - } - }); - Thread.sleep(200); // 0.2초 대기 - // PessimisticLock - executorService.submit(() -> { - try { - ticketCrudService.updateTicketRemainingStockWithPLock(List.of(ticketId), 1); - } catch (Exception e) { - throw new ConcurrencyFailureException("Failed to update ticket"); - } finally { - endLatch.countDown(); - } - }); - endLatch.await(); - - var ticket = ticketRepository.findAll().getFirst(); - System.out.println( - "Ticket remainingStock : " + ticket.getRemainingStock()); - } } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java index 0e72b4db..af86abf3 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/TicketingTestHelper.java @@ -5,7 +5,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.TestComponent; -import org.springframework.transaction.annotation.Transactional; import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; @@ -23,12 +22,6 @@ public class TicketingTestHelper { @Autowired private MemberRepository memberRepository; - @Transactional - public UUID createTicketingAndReturnId(UUID memberId, int saleStartAfterYears, int stock) { - var ticketing = createTicketing(memberId, saleStartAfterYears, stock); - return ticketing.getId(); - } - public Ticketing createTicketing(UUID memberId, int saleStartAfterYears) { var now = LocalDateTime.now(); var eventTime = now.plusYears(saleStartAfterYears + 2); diff --git a/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelper.java b/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelper.java index 74a2a97e..9708a355 100644 --- a/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelper.java +++ b/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelper.java @@ -2,7 +2,6 @@ import java.time.LocalDateTime; import java.util.List; -import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.TestComponent; @@ -168,12 +167,6 @@ public Member createMember(String email, String password, RoleEnum roleEnum, lon .build()); } - @Transactional - public UUID createMemberAndReturnId(String email) { - var member = createMember(email); - return member.getId(); - } - public ApiResponse> getDeserializedListApiResponse(String json, Class responseType) throws JsonProcessingException { return objectMapper.readValue(json, getListApiResponseType(responseType)); From a15ab4f8c0feede0ca4778ad394d9cbadbff0819 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Tue, 11 Jun 2024 02:48:56 +0900 Subject: [PATCH 15/38] =?UTF-8?q?purchaseService=20=EB=B0=8F=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../purchase/service/PurchaseService.java | 13 +-- .../purchase/service/PurchaseServiceTest.java | 99 ++++--------------- 2 files changed, 18 insertions(+), 94 deletions(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseService.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseService.java index 28318d7f..f3f63101 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseService.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseService.java @@ -1,7 +1,6 @@ package com.tiketeer.Tiketeer.domain.purchase.service; import java.time.LocalDateTime; -import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; @@ -12,22 +11,18 @@ import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotFoundException; import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotInSalePeriodException; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; -import com.tiketeer.Tiketeer.domain.ticket.Ticket; -import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; @Service @Transactional(readOnly = true) public class PurchaseService { - private final TicketRepository ticketRepository; private final TicketingRepository ticketingRepository; private final PurchaseRepository purchaseRepository; @Autowired - PurchaseService(TicketRepository ticketRepository, TicketingRepository ticketingRepository, + PurchaseService(TicketingRepository ticketingRepository, PurchaseRepository purchaseRepository) { - this.ticketRepository = ticketRepository; this.ticketingRepository = ticketingRepository; this.purchaseRepository = purchaseRepository; } @@ -48,10 +43,4 @@ public void validatePurchaseOwnership(UUID purchaseId, String email) { throw new AccessForNotOwnedPurchaseException(); } } - - public List findTicketsUnderPurchase(UUID purchaseId) { - var purchase = purchaseRepository.findById(purchaseId).orElseThrow(PurchaseNotFoundException::new); - var ticketsUnderPurchase = ticketRepository.findAll(); - return ticketsUnderPurchase; - } } \ No newline at end of file diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseServiceTest.java index da0f6242..c2eec9c4 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/service/PurchaseServiceTest.java @@ -1,9 +1,6 @@ package com.tiketeer.Tiketeer.domain.purchase.service; import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; @@ -13,28 +10,27 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; -import org.springframework.data.domain.Limit; -import org.springframework.data.util.Pair; import org.springframework.transaction.annotation.Transactional; -import com.tiketeer.Tiketeer.domain.member.Member; -import com.tiketeer.Tiketeer.domain.purchase.Purchase; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseTestHelper; import com.tiketeer.Tiketeer.domain.purchase.exception.AccessForNotOwnedPurchaseException; -import com.tiketeer.Tiketeer.domain.purchase.exception.NotEnoughTicketException; import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotInSalePeriodException; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; -import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; +import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; import com.tiketeer.Tiketeer.testhelper.TestHelper; -@Import({TestHelper.class}) +@Import({TestHelper.class, PurchaseTestHelper.class, TicketingTestHelper.class}) @SpringBootTest public class PurchaseServiceTest { @Autowired private TestHelper testHelper; @Autowired + private PurchaseTestHelper purchaseTestHelper; + @Autowired + private TicketingTestHelper ticketingTestHelper; + @Autowired private PurchaseService purchaseService; @Autowired private PurchaseRepository purchaseRepository; @@ -60,7 +56,7 @@ void validateTicketingSalePeriodSuccess() { // given var mockEmail = "test1@test.com"; var member = testHelper.createMember(mockEmail, "1234"); - var ticketing = createTicketing(member, 0, 1); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, 1); // when // then @@ -76,7 +72,7 @@ void validateTicketingSalePeriodThrowErr() { // given var mockEmail = "test1@test.com"; var member = testHelper.createMember(mockEmail, "1234"); - var ticketing = createTicketing(member, 1, 1); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 1, 1); Assertions.assertThatThrownBy(() -> { // when @@ -87,12 +83,14 @@ void validateTicketingSalePeriodThrowErr() { @Test @DisplayName("구매자 본인이 접근 > 구매자 소유 검증 > 통과") + @Transactional void validatePurchaseOwnershipSuccess() { // given var mockEmail = "test1@test.com"; var member = testHelper.createMember(mockEmail, "1234"); - var ticketing = createTicketing(member, 0, 1); - var purchaseTicketPair = createPurchase(member, ticketing, 1); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, 1); + var ticketId = ticketRepository.findAll().getFirst().getId(); + var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketId, 1); var purchase = purchaseTicketPair.getFirst(); // when @@ -104,12 +102,14 @@ void validatePurchaseOwnershipSuccess() { @Test @DisplayName("구매자가 아닌 사람이 접근 > 구매자 소유 검증 > Throw Err") + @Transactional void validatePurchaseOwnershipThrowErr() { // given var mockEmail = "test1@test.com"; var member = testHelper.createMember(mockEmail, "1234"); - var ticketing = createTicketing(member, 0, 1); - var purchaseTicketPair = createPurchase(member, ticketing, 1); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, 1); + var ticketId = ticketRepository.findAll().getFirst().getId(); + var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketId, 1); var purchase = purchaseTicketPair.getFirst(); Assertions.assertThatThrownBy(() -> { @@ -118,69 +118,4 @@ void validatePurchaseOwnershipThrowErr() { // then }).isInstanceOf(AccessForNotOwnedPurchaseException.class); } - - @Test - @DisplayName("구매 생성 > 구매 하위 티켓 조회 요청 > 성공") - void findTicketsUnderPurchaseSuccess() { - // given - var mockEmail = "test1@test.com"; - var member = testHelper.createMember(mockEmail, "1234"); - var ticketing = createTicketing(member, 0, 1); - var purchaseTicketPair = createPurchase(member, ticketing, 1); - var purchase = purchaseTicketPair.getFirst(); - var tickets = purchaseTicketPair.getSecond(); - - // when - var ticketsUnderPurchase = purchaseService.findTicketsUnderPurchase(purchase.getId()); - - // then - Assertions.assertThat(tickets.getFirst().getId()).isEqualTo(ticketsUnderPurchase.getFirst().getId()); - Assertions.assertThat(tickets.getFirst().getTicketing().getId()) - .isEqualTo(ticketsUnderPurchase.getFirst().getTicketing().getId()); - Assertions.assertThat(tickets.getFirst().getCreatedAt()) - .isEqualTo(ticketsUnderPurchase.getFirst().getCreatedAt()); - } - - private Ticketing createTicketing(Member member, int saleStartAfterYears, int stock) { - var now = LocalDateTime.now(); - var eventTime = now.plusYears(saleStartAfterYears + 2); - var saleStart = now.plusYears(saleStartAfterYears); - var saleEnd = now.plusYears(saleStartAfterYears + 1); - var ticketing = ticketingRepository.save(Ticketing.builder() - .title("test") - .member(member) - .description("") - .location("Seoul") - .eventTime(eventTime) - .saleStart(saleStart) - .saleEnd(saleEnd) - .category("concert") - .runningMinutes(300).build()); - ticketRepository.saveAll(Arrays.stream(new int[stock]) - .mapToObj(i -> Ticket.builder().ticketing(ticketing).build()) - .toList()); - return ticketing; - } - - private Pair> createPurchase(Member member, Ticketing ticketing, int count) { - var purchase = this.purchaseRepository.save(Purchase.builder().member(member).build()); - - if (count > 0) { - var tickets = updateTicketPurchase(purchase, ticketing, count); - return Pair.of(purchase, tickets); - } - return Pair.of(purchase, Collections.emptyList()); - } - - private List updateTicketPurchase(Purchase purchase, Ticketing ticketing, int count) { - var tickets = this.ticketRepository.findByTicketingIdOrderById(ticketing.getId(), - Limit.of(count)); - if (tickets.size() < count) { - throw new NotEnoughTicketException(); - } - tickets.forEach(ticket -> { - this.ticketRepository.save(ticket); - }); - return tickets; - } } \ No newline at end of file From cc5d1c70477d96f97f8ef1b11f66c6044cc92384 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Tue, 11 Jun 2024 19:10:42 +0900 Subject: [PATCH 16/38] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/PurchaseRepositoryTest.java | 88 ++++++++----------- 1 file changed, 38 insertions(+), 50 deletions(-) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java index dec32909..ab8f903c 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java @@ -1,8 +1,7 @@ package com.tiketeer.Tiketeer.domain.purchase.repository; -import java.lang.reflect.Method; import java.time.LocalDateTime; -import java.util.Arrays; +import java.util.function.Function; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; @@ -13,6 +12,7 @@ import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchase; import com.tiketeer.Tiketeer.domain.purchase.Purchase; import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; import com.tiketeer.Tiketeer.domain.role.Role; @@ -101,68 +101,56 @@ void findWithTicketingByMember() throws Throwable { Assertions.assertThat(results.get(0).getPurchaseId()).isEqualTo(purchase1.getId()); Assertions.assertThat(results.get(1).getPurchaseId()).isEqualTo(purchase2.getId()); - var purchasePropertyNames = Arrays.asList("ticketingId", "title", "location", "eventTime", - "createdAt", "category", "runningMinutes"); for (int i = 0; i < 2; i++) { var purchase = results.get(i); var ticketing = i == 0 ? ticketing1 : ticketing2; - for (var p : purchasePropertyNames) { - var comparablePropertyName = p.equals("ticketingId") ? "id" : p; - checkObjectProperty(purchase, p, ticketing, comparablePropertyName); - } + checkProperty(purchase, GetMemberPurchase::getTicketingId, ticketing.getId()); + checkProperty(purchase, GetMemberPurchase::getTitle, ticketing.getTitle()); + checkProperty(purchase, GetMemberPurchase::getLocation, ticketing.getLocation()); + checkProperty(purchase, GetMemberPurchase::getEventTime, ticketing.getEventTime()); + checkProperty(purchase, GetMemberPurchase::getCreatedAt, ticketing.getCreatedAt()); + checkProperty(purchase, GetMemberPurchase::getCategory, ticketing.getCategory()); + checkProperty(purchase, GetMemberPurchase::getRunningMinutes, ticketing.getRunningMinutes()); } - Assertions.assertThat(results.get(0).getPurchaseItems().size()).isEqualTo(2); - if (results.get(0).getPurchaseItems().getFirst().getPurchaseItemId().compareTo(purchaseItem1.getId()) == 0) { - Assertions.assertThat(results.getFirst().getPurchaseItems().get(1).getPurchaseItemId()) - .isEqualTo(purchaseItem2.getId()); - } else { - Assertions.assertThat(results.getFirst().getPurchaseItems().get(0).getPurchaseItemId()) - .isEqualTo(purchaseItem2.getId()); - Assertions.assertThat(results.getFirst().getPurchaseItems().get(1).getPurchaseItemId()) - .isEqualTo(purchaseItem1.getId()); - } - Assertions.assertThat(results.get(1).getPurchaseItems().getFirst().getPurchaseItemId()) - .isEqualTo(purchaseItem3.getId()); + Assertions.assertThat(results.get(0).getPurchaseItems()) + .extracting("purchaseItemId") + .containsExactlyInAnyOrder(purchaseItem1.getId(), purchaseItem2.getId()); + Assertions.assertThat(results.get(1).getPurchaseItems()) + .extracting("purchaseItemId") + .containsExactly(purchaseItem3.getId()); - var purchaseItemPropertyNames = Arrays.asList("ticketId", "title", "description", "price"); for (int i = 0; i < 2; i++) { var purchase = results.get(i); var ticket = i == 0 ? ticket1 : ticket2; - for (var p : purchaseItemPropertyNames) { - var comparablePropertyName = p.equals("ticketId") ? "id" : p; - checkObjectProperty(purchase.getPurchaseItems().getFirst(), p, ticket, comparablePropertyName); - if (i == 0) { - checkObjectProperty(purchase.getPurchaseItems().get(1), p, ticket, comparablePropertyName); - } + checkProperty(purchase.getPurchaseItems().getFirst(), + GetMemberPurchase.GetMemberPurchaseItems::getTicketId, ticket.getId()); + checkProperty(purchase.getPurchaseItems().getFirst(), + GetMemberPurchase.GetMemberPurchaseItems::getTitle, ticket.getTitle()); + checkProperty(purchase.getPurchaseItems().getFirst(), + GetMemberPurchase.GetMemberPurchaseItems::getDescription, ticket.getDescription()); + checkProperty(purchase.getPurchaseItems().getFirst(), + GetMemberPurchase.GetMemberPurchaseItems::getPrice, ticket.getPrice()); + if (i == 0) { + checkProperty(purchase.getPurchaseItems().get(1), + GetMemberPurchase.GetMemberPurchaseItems::getTicketId, ticket.getId()); + checkProperty(purchase.getPurchaseItems().get(1), + GetMemberPurchase.GetMemberPurchaseItems::getTitle, ticket.getTitle()); + checkProperty(purchase.getPurchaseItems().get(1), + GetMemberPurchase.GetMemberPurchaseItems::getDescription, ticket.getDescription()); + checkProperty(purchase.getPurchaseItems().get(1), + GetMemberPurchase.GetMemberPurchaseItems::getPrice, ticket.getPrice()); } } } - private void checkObjectProperty(Object object, String propertyName, Object comparableObj, - String comparablePropertyName) throws Throwable { - if (propertyName.isEmpty() || comparablePropertyName.isEmpty()) { - throw new Error("no property name"); - } - var methodName = "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); - Method method = object - .getClass() - .getMethod(methodName); - - var methodName2 = propertyName.equals(comparablePropertyName) ? methodName : - "get" + comparablePropertyName.substring(0, 1).toUpperCase() + comparablePropertyName.substring(1); - Method method2 = comparableObj.getClass().getMethod(methodName2); - - // 메소드 호출 - Object property = method.invoke(object); - Object comparableProperty = method2.invoke(comparableObj); - log.debug( - "checkObjectProperty : " + property + "(" + methodName + "), " + comparableProperty + "(" + methodName2 - + ")"); - if (property instanceof LocalDateTime time && comparableProperty instanceof LocalDateTime comparableTime) { - Assertions.assertThat(time).isEqualToIgnoringNanos(comparableTime); + private void checkProperty(T object, Function propertyGetter, R expectedValue) { + if (propertyGetter.apply(object) instanceof LocalDateTime time + && expectedValue instanceof LocalDateTime expectedTime) { + Assertions.assertThat(time).isEqualToIgnoringNanos(expectedTime); } else { - Assertions.assertThat(property).isEqualTo(comparableProperty); + Assertions.assertThat(propertyGetter.apply(object)).isEqualTo(expectedValue); } + } } From e49380ba1ed042e3b2b84ad46a2920830ea61693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B0=B0=ED=9D=99=EA=B3=A0=EC=96=91=EC=9D=B4?= Date: Tue, 11 Jun 2024 19:47:24 +0900 Subject: [PATCH 17/38] =?UTF-8?q?TicketingCrudService=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Tiketeer/domain/ticket/Ticket.java | 18 ++++++++- .../ticket/service/TicketCrudService.java | 21 +++++++--- .../usecase/dto/CreateTicketCommandDto.java | 13 +++---- .../usecase/dto/CreateTicketMetadata.java | 25 ++++++++++++ .../Tiketeer/domain/ticketing/Ticketing.java | 9 ++++- .../ticketing/service/TicketingService.java | 10 +---- .../service/TicketingStockService.java | 4 +- .../ticket/service/TicketCrudServiceTest.java | 38 ++++++++++--------- 8 files changed, 92 insertions(+), 46 deletions(-) create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketMetadata.java diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java index a25d9b38..e1d4f29c 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java @@ -73,11 +73,25 @@ public class Ticket { private Integer version; @Builder - public Ticket(long price, int stock, int remainingStock, Ticketing ticketing) { + public Ticket(long price, int stock, int remainingStock, String title, String description, Ticketing ticketing) { this.price = price; this.stock = stock; this.remainingStock = remainingStock; - this.ticketing = ticketing; + this.title = title; + this.description = description; + setTicketing(ticketing); } + public void setTicketing(Ticketing ticketing) { + if (this.ticketing != null) { + this.ticketing.getTickets().remove(this); + } + this.ticketing = ticketing; + + if (ticketing == null) { + return; + } + + this.ticketing.getTickets().add(this); + } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java index 91100696..221a273a 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java @@ -1,6 +1,5 @@ package com.tiketeer.Tiketeer.domain.ticket.service; -import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -10,6 +9,7 @@ import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketCommandDto; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; @@ -32,12 +32,21 @@ public List listTicketByTicketingId(UUID ticketingId) { } @Transactional - public void createTickets(UUID ticketingId, int numOfTickets) { - var ticketing = findTicketingById(ticketingId); - - ticketRepository.saveAll(Arrays.stream(new int[numOfTickets]) - .mapToObj(i -> Ticket.builder().ticketing(ticketing).build()) + public void createTickets(CreateTicketCommandDto dto) { + var ticketing = findTicketingById(dto.getTicketingId()); + + ticketRepository.saveAll(dto.getCreateTicketMetadataList().stream() + .map(meta -> Ticket.builder() + .ticketing(ticketing) + .price(meta.getPrice()) + .stock(meta.getStock()) + .remainingStock(meta.getRemainingStock()) + .title(meta.getTitle()) + .description(meta.getDescription()) + .build() + ) .toList()); + } @Transactional diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketCommandDto.java index 683479ba..9b5139ff 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketCommandDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketCommandDto.java @@ -1,6 +1,6 @@ package com.tiketeer.Tiketeer.domain.ticket.usecase.dto; -import java.time.LocalDateTime; +import java.util.List; import java.util.UUID; import lombok.Builder; @@ -11,15 +11,12 @@ @ToString public class CreateTicketCommandDto { private final UUID ticketingId; - private final int numOfTickets; - private LocalDateTime commandCreatedAt = LocalDateTime.now(); + + private final List createTicketMetadataList; @Builder - public CreateTicketCommandDto(UUID ticketingId, int numOfTickets, LocalDateTime commandCreatedAt) { + public CreateTicketCommandDto(UUID ticketingId, List createTicketMetadataList) { this.ticketingId = ticketingId; - this.numOfTickets = numOfTickets; - if (commandCreatedAt != null) { - this.commandCreatedAt = commandCreatedAt; - } + this.createTicketMetadataList = createTicketMetadataList; } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketMetadata.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketMetadata.java new file mode 100644 index 00000000..edc7c9c1 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketMetadata.java @@ -0,0 +1,25 @@ +package com.tiketeer.Tiketeer.domain.ticket.usecase.dto; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public class CreateTicketMetadata { + + private final int stock; + private final int remainingStock; + private final String title; + private final String description; + private final long price; + + @Builder + public CreateTicketMetadata(int stock, int remainingStock, String title, String description, long price) { + this.stock = stock; + this.remainingStock = remainingStock; + this.title = title; + this.description = description; + this.price = price; + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/Ticketing.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/Ticketing.java index c42ce907..22fdc74d 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/Ticketing.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/Ticketing.java @@ -1,6 +1,8 @@ package com.tiketeer.Tiketeer.domain.ticketing; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import org.hibernate.annotations.UuidGenerator; @@ -9,6 +11,7 @@ import com.tiketeer.Tiketeer.constant.StorageEnum; import com.tiketeer.Tiketeer.domain.member.Member; +import com.tiketeer.Tiketeer.domain.ticket.Ticket; import jakarta.persistence.Column; import jakarta.persistence.ConstraintMode; @@ -21,6 +24,7 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; @@ -29,14 +33,12 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import lombok.ToString; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Table(name = "ticketings") @EntityListeners(AuditingEntityListener.class) @Getter -@ToString public class Ticketing { @Id @UuidGenerator @@ -98,6 +100,9 @@ public class Ticketing { @Column(name = "created_at", nullable = false) private LocalDateTime createdAt; + @OneToMany(mappedBy = "ticketing") + private List tickets = new ArrayList<>(); + @Builder public Ticketing( Member member, diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingService.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingService.java index fc9eddfa..60c778b0 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingService.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingService.java @@ -8,8 +8,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticket.service.TicketCrudService; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.exception.EventTimeNotValidException; import com.tiketeer.Tiketeer.domain.ticketing.exception.ModifyForNotOwnedTicketingException; @@ -21,15 +19,10 @@ @Transactional(readOnly = true) public class TicketingService { private final TicketingRepository ticketingRepository; - private final TicketCrudService ticketCrudService; - private final TicketRepository ticketRepository; @Autowired - public TicketingService(TicketingRepository ticketingRepository, TicketCrudService ticketCrudService, - TicketRepository ticketRepository) { + public TicketingService(TicketingRepository ticketingRepository) { this.ticketingRepository = ticketingRepository; - this.ticketCrudService = ticketCrudService; - this.ticketRepository = ticketRepository; } public Ticketing findById(UUID ticketingId) { @@ -77,6 +70,5 @@ public void validateTicketingOwnership(Ticketing ticketing, String email) { if (!ticketing.getMember().getEmail().equals(email)) { throw new ModifyForNotOwnedTicketingException(); } - return; } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockService.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockService.java index 511e20fd..cae8cd82 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockService.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockService.java @@ -8,6 +8,7 @@ import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.service.TicketCrudService; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketCommandDto; @Service @Transactional(readOnly = true) @@ -24,7 +25,8 @@ public TicketingStockService(TicketCrudService ticketCrudService, TicketingServi @Transactional public void createStock(UUID ticketingId, int stock) { var ticketing = ticketingService.findById(ticketingId); - ticketCrudService.createTickets(ticketing.getId(), stock); + //TODO 마이그레이션 반영 필요 + ticketCrudService.createTickets(CreateTicketCommandDto.builder().build()); } @Transactional diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java index a4f31b61..ccc45e99 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java @@ -15,14 +15,11 @@ import org.springframework.transaction.annotation.Transactional; import com.tiketeer.Tiketeer.domain.member.Member; -import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; -import com.tiketeer.Tiketeer.domain.role.repository.RoleRepository; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; -import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingService; import com.tiketeer.Tiketeer.testhelper.TestHelper; @Import({TestHelper.class}) @@ -34,12 +31,6 @@ public class TicketCrudServiceTest { @Autowired private TicketCrudService ticketCrudService; @Autowired - private TicketingService ticketingService; - @Autowired - private MemberRepository memberRepository; - @Autowired - private RoleRepository roleRepository; - @Autowired private TicketRepository ticketRepository; @Autowired private TicketingRepository ticketingRepository; @@ -79,11 +70,11 @@ void listTicketByTicketingSuccess() { var mockStock = 30; - var ticketingId = createTicketingAndReturnId(member, mockStock, now.plusYears(1), now.plusYears(2), - now.plusYears(3)); + var ticketing = createTicketing(member, now.plusYears(1), now.plusYears(2), now.plusYears(3)); + createTickets(ticketing, mockStock, mockStock, "", "", 1000); // when - var tickets = ticketCrudService.listTicketByTicketingId(ticketingId); + var tickets = ticketCrudService.listTicketByTicketingId(ticketing.getId()); // then Assertions.assertThat(tickets.size()).isEqualTo(mockStock); @@ -112,8 +103,8 @@ void createTicketsSuccess() { var member = testHelper.createMember(mockEmail); var mockStock = 10; - var ticketingId = createTicketingAndReturnId(member, mockStock, now.plusYears(1), now.plusYears(2), - now.plusYears(3)); + var ticketing = createTicketing(member, now.plusYears(1), now.plusYears(2), now.plusYears(3)); + createTickets(ticketing, mockStock, mockStock, "", "", 1000); var addTickets = 20; @@ -125,9 +116,10 @@ void createTicketsSuccess() { Assertions.assertThat(tickets.size()).isEqualTo(mockStock + addTickets); } - private UUID createTicketingAndReturnId(Member member, int stock, LocalDateTime saleStart, LocalDateTime saleEnd, + private Ticketing createTicketing(Member member, LocalDateTime saleStart, + LocalDateTime saleEnd, LocalDateTime eventTime) { - var ticketing = ticketingRepository.save(Ticketing.builder() + return ticketingRepository.save(Ticketing.builder() .member(member) .title("음악회") .location("서울 강남역 8번 출구") @@ -136,9 +128,19 @@ private UUID createTicketingAndReturnId(Member member, int stock, LocalDateTime .saleStart(saleStart) .saleEnd(saleEnd) .eventTime(eventTime).build()); + + } + + private void createTickets(Ticketing ticketing, int stock, int remainingStock, String title, String description, + long price) { ticketRepository.saveAll(Arrays.stream(new int[stock]) - .mapToObj(i -> Ticket.builder().ticketing(ticketing).build()) + .mapToObj(i -> Ticket.builder() + .ticketing(ticketing) + .price(price) + .title(title) + .description(description) + .remainingStock(remainingStock) + .build()) .toList()); - return ticketing.getId(); } } From 90e7601b716549035a7ab5d27c6d784533e10467 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Tue, 11 Jun 2024 19:53:11 +0900 Subject: [PATCH 18/38] =?UTF-8?q?GetMemberPurchase=20EP=20->=20GetMemberAl?= =?UTF-8?q?lPurcahses,=20GetMemberPurcahse=20EP=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 34 +++-- .../dto/GetMemberAllPurchasesResponseDto.java | 51 +++++++ ...java => GetMemberPurchaseResponseDto.java} | 14 +- ...java => GetMemberAllPurchasesUseCase.java} | 12 +- .../usecase/GetMemberPurchaseUseCase.java | 34 +++++ ...a => GetMemberAllPurchasesCommandDto.java} | 4 +- .../dto/GetMemberAllPurchasesResultDto.java | 78 ++++++++++ .../member/usecase/dto/GetMemberPurchase.java | 3 + .../dto/GetMemberPurchaseCommandDto.java | 20 +++ ...o.java => GetMemberPurchaseResultDto.java} | 11 +- .../controller/MemberControllerTest.java | 79 +++++++++- ... => GetMemberAllPurchasesUseCaseTest.java} | 14 +- .../usecase/GetMemberPurchaseUseCaseTest.java | 143 ++++++++++++++++++ 13 files changed, 457 insertions(+), 40 deletions(-) create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberAllPurchasesResponseDto.java rename src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/{GetMemberPurchasesResponseDto.java => GetMemberPurchaseResponseDto.java} (73%) rename src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/{GetMemberPurchasesUseCase.java => GetMemberAllPurchasesUseCase.java} (73%) create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchaseUseCase.java rename src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/{GetMemberPurchasesCommandDto.java => GetMemberAllPurchasesCommandDto.java} (67%) create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberAllPurchasesResultDto.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchaseCommandDto.java rename src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/{GetMemberPurchasesResultDto.java => GetMemberPurchaseResultDto.java} (84%) rename src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/{GetMemberPurchasesUseCaseTest.java => GetMemberAllPurchasesUseCaseTest.java} (90%) create mode 100644 src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchaseUseCaseTest.java diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/MemberController.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/MemberController.java index 0aa273c7..e3eedeed 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/MemberController.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/MemberController.java @@ -18,7 +18,8 @@ import com.tiketeer.Tiketeer.auth.SecurityContextHelper; import com.tiketeer.Tiketeer.domain.member.controller.dto.ChargePointRequestDto; import com.tiketeer.Tiketeer.domain.member.controller.dto.ChargePointResponseDto; -import com.tiketeer.Tiketeer.domain.member.controller.dto.GetMemberPurchasesResponseDto; +import com.tiketeer.Tiketeer.domain.member.controller.dto.GetMemberAllPurchasesResponseDto; +import com.tiketeer.Tiketeer.domain.member.controller.dto.GetMemberPurchaseResponseDto; import com.tiketeer.Tiketeer.domain.member.controller.dto.GetMemberResponseDto; import com.tiketeer.Tiketeer.domain.member.controller.dto.GetMemberTicketingSalesResponseDto; import com.tiketeer.Tiketeer.domain.member.controller.dto.MemberRegisterRequestDto; @@ -26,15 +27,17 @@ import com.tiketeer.Tiketeer.domain.member.controller.dto.ResetPasswordRequestDto; import com.tiketeer.Tiketeer.domain.member.usecase.ChargeMemberPointUseCase; import com.tiketeer.Tiketeer.domain.member.usecase.DeleteMemberUseCase; -import com.tiketeer.Tiketeer.domain.member.usecase.GetMemberPurchasesUseCase; +import com.tiketeer.Tiketeer.domain.member.usecase.GetMemberAllPurchasesUseCase; +import com.tiketeer.Tiketeer.domain.member.usecase.GetMemberPurchaseUseCase; import com.tiketeer.Tiketeer.domain.member.usecase.GetMemberTicketingSalesUseCase; import com.tiketeer.Tiketeer.domain.member.usecase.GetMemberUseCase; import com.tiketeer.Tiketeer.domain.member.usecase.MemberRegisterUseCase; import com.tiketeer.Tiketeer.domain.member.usecase.ResetPasswordUseCase; import com.tiketeer.Tiketeer.domain.member.usecase.SendPasswordChangeEmailUseCase; import com.tiketeer.Tiketeer.domain.member.usecase.dto.DeleteMemberCommandDto; +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberAllPurchasesCommandDto; import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberCommandDto; -import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesCommandDto; +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchaseCommandDto; import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberTicketingSalesCommandDto; import com.tiketeer.Tiketeer.domain.member.usecase.dto.MemberRegisterCommandDto; import com.tiketeer.Tiketeer.domain.member.usecase.dto.SendPwdChangeEmailCommandDto; @@ -49,7 +52,8 @@ public class MemberController { private final ChargeMemberPointUseCase chargeMemberPointUseCase; private final GetMemberTicketingSalesUseCase getMemberTicketingSalesUseCase; private final GetMemberUseCase getMemberUseCase; - private final GetMemberPurchasesUseCase getMemberPurchasesUseCase; + private final GetMemberAllPurchasesUseCase getMemberAllPurchasesUseCase; + private final GetMemberPurchaseUseCase getMemberPurchasesUseCase; private final ResetPasswordUseCase resetPasswordUseCase; private final SendPasswordChangeEmailUseCase sendPasswordChangeEmailUseCase; private final DeleteMemberUseCase deleteMemberUseCase; @@ -59,7 +63,8 @@ public class MemberController { public MemberController(MemberRegisterUseCase memberRegisterUseCase, ChargeMemberPointUseCase chargeMemberPointUseCase, ResetPasswordUseCase resetPasswordUseCase, GetMemberTicketingSalesUseCase getMemberTicketingSalesUseCase, - GetMemberUseCase getMemberUseCase, GetMemberPurchasesUseCase getMemberPurchasesUseCase, + GetMemberUseCase getMemberUseCase, GetMemberAllPurchasesUseCase getMemberAllPurchasesUseCase, + GetMemberPurchaseUseCase getMemberPurchasesUseCase, SendPasswordChangeEmailUseCase sendPasswordChangeEmailUseCase, DeleteMemberUseCase deleteMemberUseCase, SecurityContextHelper securityContextHelper) { @@ -68,6 +73,7 @@ public MemberController(MemberRegisterUseCase memberRegisterUseCase, this.getMemberTicketingSalesUseCase = getMemberTicketingSalesUseCase; this.getMemberUseCase = getMemberUseCase; this.resetPasswordUseCase = resetPasswordUseCase; + this.getMemberAllPurchasesUseCase = getMemberAllPurchasesUseCase; this.getMemberPurchasesUseCase = getMemberPurchasesUseCase; this.sendPasswordChangeEmailUseCase = sendPasswordChangeEmailUseCase; this.deleteMemberUseCase = deleteMemberUseCase; @@ -105,13 +111,23 @@ public ResponseEntity> chargePoint(@PathVari } @GetMapping("/{memberId}/purchases") - public ResponseEntity>> getMemberPurchases( + public ResponseEntity>> getMemberAllPurchases( @PathVariable UUID memberId) { var email = securityContextHelper.getEmailInToken(); - var results = getMemberPurchasesUseCase.getMemberPurchases( - GetMemberPurchasesCommandDto.builder().memberEmail(email).build()); + var results = getMemberAllPurchasesUseCase.getMemberPurchases( + GetMemberAllPurchasesCommandDto.builder().memberEmail(email).build()); var responseBody = ApiResponse.wrap( - results.stream().map(GetMemberPurchasesResponseDto::convertFromDto).toList()); + results.stream().map(GetMemberAllPurchasesResponseDto::convertFromDto).toList()); + return ResponseEntity.status(HttpStatus.OK).body(responseBody); + } + + @GetMapping("/{memberId}/purchases/{purchaseId}") + public ResponseEntity> getMemberPurchase( + @PathVariable UUID memberId, @PathVariable UUID purchaseId) { + var email = securityContextHelper.getEmailInToken(); + var result = getMemberPurchasesUseCase.getMemberPurchases( + GetMemberPurchaseCommandDto.builder().memberEmail(email).purchaseId(purchaseId).build()); + var responseBody = ApiResponse.wrap(GetMemberPurchaseResponseDto.convertFromDto(result)); return ResponseEntity.status(HttpStatus.OK).body(responseBody); } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberAllPurchasesResponseDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberAllPurchasesResponseDto.java new file mode 100644 index 00000000..c9a9628d --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberAllPurchasesResponseDto.java @@ -0,0 +1,51 @@ +package com.tiketeer.Tiketeer.domain.member.controller.dto; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberAllPurchasesResultDto; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public final class GetMemberAllPurchasesResponseDto { + private final UUID purchaseId; + private final UUID ticketingId; + private final String title; + private final LocalDateTime eventTime; + private final LocalDateTime createdAt; + private final String category; + private final String thumbnailPath; + private final List purchaseItems; + + @Builder + public GetMemberAllPurchasesResponseDto(UUID purchaseId, UUID ticketingId, String title, + LocalDateTime eventTime, LocalDateTime createdAt, String category, String thumbnailPath, + List purchaseItems) { + this.purchaseId = purchaseId; + this.ticketingId = ticketingId; + this.title = title; + this.eventTime = eventTime; + this.createdAt = createdAt; + this.category = category; + this.thumbnailPath = thumbnailPath; + this.purchaseItems = purchaseItems; + } + + public static GetMemberAllPurchasesResponseDto convertFromDto(GetMemberAllPurchasesResultDto dto) { + return GetMemberAllPurchasesResponseDto.builder() + .purchaseId(dto.getPurchaseId()) + .ticketingId(dto.getTicketingId()) + .title(dto.getTitle()) + .eventTime(dto.getEventTime()) + .createdAt(dto.getCreatedAt()) + .category(dto.getCategory()) + .thumbnailPath(dto.getThumbnailPath()) + .purchaseItems(dto.getPurchaseItems()) + .build(); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchasesResponseDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchaseResponseDto.java similarity index 73% rename from src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchasesResponseDto.java rename to src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchaseResponseDto.java index ab7423ba..31039e75 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchasesResponseDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchaseResponseDto.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.UUID; -import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesResultDto; +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchaseResultDto; import lombok.Builder; import lombok.Getter; @@ -12,7 +12,7 @@ @Getter @ToString -public final class GetMemberPurchasesResponseDto { +public final class GetMemberPurchaseResponseDto { private final UUID purchaseId; private final UUID ticketingId; private final String title; @@ -21,13 +21,13 @@ public final class GetMemberPurchasesResponseDto { private final LocalDateTime createdAt; private final String category; private final int runningMinute; - private final List purchaseItems; + private final List purchaseItems; @Builder - public GetMemberPurchasesResponseDto(UUID purchaseId, UUID ticketingId, String title, String location, + public GetMemberPurchaseResponseDto(UUID purchaseId, UUID ticketingId, String title, String location, LocalDateTime eventTime, LocalDateTime createdAt, String category, int runningMinute, - List purchaseItems) { + List purchaseItems) { this.purchaseId = purchaseId; this.ticketingId = ticketingId; this.title = title; @@ -39,8 +39,8 @@ public GetMemberPurchasesResponseDto(UUID purchaseId, UUID ticketingId, String t this.purchaseItems = purchaseItems; } - public static GetMemberPurchasesResponseDto convertFromDto(GetMemberPurchasesResultDto dto) { - return GetMemberPurchasesResponseDto.builder() + public static GetMemberPurchaseResponseDto convertFromDto(GetMemberPurchaseResultDto dto) { + return GetMemberPurchaseResponseDto.builder() .purchaseId(dto.getPurchaseId()) .ticketingId(dto.getTicketingId()) .title(dto.getTitle()) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberAllPurchasesUseCase.java similarity index 73% rename from src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCase.java rename to src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberAllPurchasesUseCase.java index baec92e9..6bf33c4a 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberAllPurchasesUseCase.java @@ -8,25 +8,25 @@ import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; -import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesCommandDto; -import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesResultDto; +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberAllPurchasesCommandDto; +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberAllPurchasesResultDto; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; @Service @Transactional(readOnly = true) -public class GetMemberPurchasesUseCase { +public class GetMemberAllPurchasesUseCase { private final MemberRepository memberRepository; private final PurchaseRepository purchaseRepository; @Autowired - public GetMemberPurchasesUseCase(MemberRepository memberRepository, PurchaseRepository purchaseRepository) { + public GetMemberAllPurchasesUseCase(MemberRepository memberRepository, PurchaseRepository purchaseRepository) { this.memberRepository = memberRepository; this.purchaseRepository = purchaseRepository; } - public List getMemberPurchases(GetMemberPurchasesCommandDto command) { + public List getMemberPurchases(GetMemberAllPurchasesCommandDto command) { var member = memberRepository.findByEmail(command.getMemberEmail()).orElseThrow(MemberNotFoundException::new); var results = purchaseRepository.findWithTicketingByMember(member); - return results.stream().map(GetMemberPurchasesResultDto::new).toList(); + return results.stream().map(GetMemberAllPurchasesResultDto::new).toList(); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchaseUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchaseUseCase.java new file mode 100644 index 00000000..26585f48 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchaseUseCase.java @@ -0,0 +1,34 @@ +package com.tiketeer.Tiketeer.domain.member.usecase; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; +import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchaseCommandDto; +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchaseResultDto; +import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotFoundException; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; + +@Service +@Transactional(readOnly = true) +public class GetMemberPurchaseUseCase { + private final MemberRepository memberRepository; + private final PurchaseRepository purchaseRepository; + + @Autowired + public GetMemberPurchaseUseCase(MemberRepository memberRepository, PurchaseRepository purchaseRepository) { + this.memberRepository = memberRepository; + this.purchaseRepository = purchaseRepository; + } + + public GetMemberPurchaseResultDto getMemberPurchases(GetMemberPurchaseCommandDto command) { + var member = memberRepository.findByEmail(command.getMemberEmail()).orElseThrow(MemberNotFoundException::new); + var results = purchaseRepository.findWithTicketingByMember(member); + if (results.isEmpty()) { + throw new PurchaseNotFoundException(); + } + return new GetMemberPurchaseResultDto(results.getFirst()); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberAllPurchasesCommandDto.java similarity index 67% rename from src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesCommandDto.java rename to src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberAllPurchasesCommandDto.java index 6fd2d0d2..649822f8 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesCommandDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberAllPurchasesCommandDto.java @@ -6,11 +6,11 @@ @Getter @ToString -public class GetMemberPurchasesCommandDto { +public class GetMemberAllPurchasesCommandDto { private final String memberEmail; @Builder - public GetMemberPurchasesCommandDto(String memberEmail) { + public GetMemberAllPurchasesCommandDto(String memberEmail) { this.memberEmail = memberEmail; } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberAllPurchasesResultDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberAllPurchasesResultDto.java new file mode 100644 index 00000000..ab4bb10b --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberAllPurchasesResultDto.java @@ -0,0 +1,78 @@ +package com.tiketeer.Tiketeer.domain.member.usecase.dto; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@ToString +public final class GetMemberAllPurchasesResultDto { + private final UUID purchaseId; + private final UUID ticketingId; + private final String title; + private final LocalDateTime eventTime; + private final LocalDateTime createdAt; + private final String category; + private final String thumbnailPath; + private final List purchaseItems; + + @Builder + public GetMemberAllPurchasesResultDto(UUID purchaseId, UUID ticketingId, String title, String location, + LocalDateTime eventTime, LocalDateTime createdAt, String category, + String thumbnailPath, List purchaseItems) { + this.purchaseId = purchaseId; + this.ticketingId = ticketingId; + this.title = title; + this.eventTime = eventTime; + this.createdAt = createdAt; + this.category = category; + this.thumbnailPath = thumbnailPath; + this.purchaseItems = purchaseItems; + } + + @Builder + public GetMemberAllPurchasesResultDto(GetMemberPurchase purchase) { + this.purchaseId = purchase.getPurchaseId(); + this.ticketingId = purchase.getTicketingId(); + this.title = purchase.getTitle(); + this.eventTime = purchase.getEventTime(); + this.category = purchase.getCategory(); + this.createdAt = purchase.getCreatedAt(); + this.thumbnailPath = purchase.getThumbnailPath(); + this.purchaseItems = purchase.getPurchaseItems().stream().map(PurchaseItem::new).toList(); + + } + + @Getter + @ToString + @NoArgsConstructor(force = true) + public static class PurchaseItem { + private final UUID purchaseItemId; + private final UUID ticketId; + private final String title; + private final Long price; + + @Builder + public PurchaseItem(UUID purchaseItemId, UUID ticketId, String title, Long price) { + this.purchaseItemId = purchaseItemId; + this.ticketId = ticketId; + this.title = title; + this.price = price; + } + + @Builder + public PurchaseItem(GetMemberPurchase.GetMemberPurchaseItems pi) { + this.purchaseItemId = pi.getPurchaseItemId(); + this.ticketId = pi.getTicketId(); + this.title = pi.getTitle(); + this.price = pi.getPrice(); + } + } + +} + diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchase.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchase.java index 5810ce5e..dc528632 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchase.java @@ -31,6 +31,9 @@ public interface GetMemberPurchase { @Value("#{target.purchaseItems[0].ticket.ticketing.runningMinutes}") int getRunningMinutes(); + @Value("#{target.purchaseItems[0].ticket.ticketing.thumbnailPath}") + String getThumbnailPath(); + List getPurchaseItems(); interface GetMemberPurchaseItems { diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchaseCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchaseCommandDto.java new file mode 100644 index 00000000..2fa65ad9 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchaseCommandDto.java @@ -0,0 +1,20 @@ +package com.tiketeer.Tiketeer.domain.member.usecase.dto; + +import java.util.UUID; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public class GetMemberPurchaseCommandDto { + private final String memberEmail; + private final UUID purchaseId; + + @Builder + public GetMemberPurchaseCommandDto(String memberEmail, UUID purchaseId) { + this.memberEmail = memberEmail; + this.purchaseId = purchaseId; + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesResultDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchaseResultDto.java similarity index 84% rename from src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesResultDto.java rename to src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchaseResultDto.java index 5dac3b9f..5fbad686 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchasesResultDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberPurchaseResultDto.java @@ -11,7 +11,7 @@ @Getter @ToString -public final class GetMemberPurchasesResultDto { +public final class GetMemberPurchaseResultDto { private final UUID purchaseId; private final UUID ticketingId; private final String title; @@ -20,12 +20,13 @@ public final class GetMemberPurchasesResultDto { private final LocalDateTime createdAt; private final String category; private final int runningMinutes; + private final String thumbnailPath; private final List purchaseItems; @Builder - public GetMemberPurchasesResultDto(UUID purchaseId, UUID ticketingId, String title, String location, + public GetMemberPurchaseResultDto(UUID purchaseId, UUID ticketingId, String title, String location, LocalDateTime eventTime, LocalDateTime createdAt, String category, - int runningMinutes, List purchaseItems) { + int runningMinutes, String thumbnailPath, List purchaseItems) { this.purchaseId = purchaseId; this.ticketingId = ticketingId; this.title = title; @@ -34,11 +35,12 @@ public GetMemberPurchasesResultDto(UUID purchaseId, UUID ticketingId, String tit this.createdAt = createdAt; this.category = category; this.runningMinutes = runningMinutes; + this.thumbnailPath = thumbnailPath; this.purchaseItems = purchaseItems; } @Builder - public GetMemberPurchasesResultDto(GetMemberPurchase purchase) { + public GetMemberPurchaseResultDto(GetMemberPurchase purchase) { this.purchaseId = purchase.getPurchaseId(); this.ticketingId = purchase.getTicketingId(); this.title = purchase.getTitle(); @@ -47,6 +49,7 @@ public GetMemberPurchasesResultDto(GetMemberPurchase purchase) { this.category = purchase.getCategory(); this.createdAt = purchase.getCreatedAt(); this.runningMinutes = purchase.getRunningMinutes(); + this.thumbnailPath = purchase.getThumbnailPath(); this.purchaseItems = purchase.getPurchaseItems().stream().map(PurchaseItem::new).toList(); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java index b472e935..184a4c88 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java @@ -30,7 +30,8 @@ import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.member.Otp; import com.tiketeer.Tiketeer.domain.member.controller.dto.ChargePointRequestDto; -import com.tiketeer.Tiketeer.domain.member.controller.dto.GetMemberPurchasesResponseDto; +import com.tiketeer.Tiketeer.domain.member.controller.dto.GetMemberAllPurchasesResponseDto; +import com.tiketeer.Tiketeer.domain.member.controller.dto.GetMemberPurchaseResponseDto; import com.tiketeer.Tiketeer.domain.member.controller.dto.GetMemberResponseDto; import com.tiketeer.Tiketeer.domain.member.controller.dto.GetMemberTicketingSalesResponseDto; import com.tiketeer.Tiketeer.domain.member.controller.dto.ResetPasswordRequestDto; @@ -332,9 +333,9 @@ void getMemberSuccess() throws Exception { } @Test - @DisplayName("정상 조건 > 멤버 구매 내역 조회 요청 > 성공") + @DisplayName("정상 조건 > 멤버 구매 내역 리스트 조회 요청 > 성공") @Transactional - void getMemberPurchasesSuccess() throws Exception { + void getMemberAllPurchasesSuccess() throws Exception { // given var now = LocalDateTime.of(2024, 1, 1, 1, 1, 1); String token = testHelper.registerAndLoginAndReturnAccessToken("user@example.com", RoleEnum.SELLER); @@ -388,8 +389,8 @@ void getMemberPurchasesSuccess() throws Exception { //then .andExpect(status().isOk()); - ApiResponse> apiResponse = testHelper.getDeserializedListApiResponse( - result.andReturn().getResponse().getContentAsString(), GetMemberPurchasesResponseDto.class); + ApiResponse> apiResponse = testHelper.getDeserializedListApiResponse( + result.andReturn().getResponse().getContentAsString(), GetMemberAllPurchasesResponseDto.class); var purchases = apiResponse.getData(); var findPurchase1 = purchases.stream() @@ -408,6 +409,74 @@ void getMemberPurchasesSuccess() throws Exception { Assertions.assertThat(findPurchase2.getFirst().getPurchaseItems().size()).isEqualTo(1); } + @Test + @DisplayName("정상 조건 > 멤버 구매 내역 단건 조회 요청 > 성공") + @Transactional + void getMemberPurchaseSuccess() throws Exception { + // given + var now = LocalDateTime.of(2024, 1, 1, 1, 1, 1); + String token = testHelper.registerAndLoginAndReturnAccessToken("user@example.com", RoleEnum.SELLER); + Member member = memberRepository.findAll().getFirst(); + Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); + + var ticketing1 = ticketingRepository.save( + Ticketing.builder() + .member(member) + .description("") + .title("test1") + .location("Seoul") + .eventTime(now) + .category("") + .runningMinutes(600) + .saleStart(now) + .saleEnd(now) + .build() + ); + var ticketing2 = ticketingRepository.save( + Ticketing.builder() + .member(member) + .description("") + .title("test2") + .location("Seoul") + .eventTime(now) + .category("") + .runningMinutes(600) + .saleStart(now) + .saleEnd(now) + .build() + ); + var purchase1 = purchaseRepository.save(new Purchase(member)); + var purchase2 = purchaseRepository.save(new Purchase(member)); + var ticket1 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket1", "", ticketing1)); + var ticket2 = ticketRepository.save(new Ticket(1000, 1, 1, "ticket2", "", ticketing1)); + var ticket3 = ticketRepository.save(new Ticket(1000, 2, 2, "ticket3", "", ticketing2)); + + purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); + purchaseItemRepository.save(new PurchaseItem(purchase1, ticket2)); + purchaseItemRepository.save(new PurchaseItem(purchase2, ticket3)); + + // when + var result = mockMvc.perform( + get("/api/members/{memberId}/purchases/{purchaseId}", member.getId(), purchase1.getId()) + .contextPath("/api") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding("utf-8") + .cookie(cookie) + ) + //then + .andExpect(status().isOk()); + + ApiResponse apiResponse = testHelper.getDeserializedApiResponse( + result.andReturn().getResponse().getContentAsString(), GetMemberPurchaseResponseDto.class); + + var purchase = apiResponse.getData(); + Assertions.assertThat(purchase.getPurchaseId()).isEqualTo(purchase1.getId()); + Assertions.assertThat(purchase.getTicketingId()).isEqualTo(ticketing1.getId()); + Assertions.assertThat(purchase.getTitle()).isEqualTo(ticketing1.getTitle()); + Assertions.assertThat(purchase.getPurchaseItems().size()).isEqualTo(2); + } + @Test @DisplayName("존재하지 않는 멤버 > 삭제 요청 > NOT_FOUND") void deleteMemberFailBecauseNotExistMember() throws Exception { diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberAllPurchasesUseCaseTest.java similarity index 90% rename from src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java rename to src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberAllPurchasesUseCaseTest.java index 3bdf1e6c..c25ef4c1 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberAllPurchasesUseCaseTest.java @@ -13,7 +13,7 @@ import org.springframework.transaction.annotation.Transactional; import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; -import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchasesCommandDto; +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberAllPurchasesCommandDto; import com.tiketeer.Tiketeer.domain.purchase.Purchase; import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; @@ -26,12 +26,12 @@ @Import({TestHelper.class}) @SpringBootTest -public class GetMemberPurchasesUseCaseTest { +public class GetMemberAllPurchasesUseCaseTest { @Autowired private TestHelper testHelper; @Autowired - private GetMemberPurchasesUseCase getMemberPurchasesUseCase; + private GetMemberAllPurchasesUseCase getMemberAllPurchasesUseCase; @Autowired private TicketingRepository ticketingRepository; @@ -99,8 +99,8 @@ void getMemberPurchases() { purchaseItemRepository.save(new PurchaseItem(purchase2, ticket3)); // when - var results = getMemberPurchasesUseCase.getMemberPurchases( - GetMemberPurchasesCommandDto.builder().memberEmail(mockEmail).build()); + var results = getMemberAllPurchasesUseCase.getMemberPurchases( + GetMemberAllPurchasesCommandDto.builder().memberEmail(mockEmail).build()); var findPurchase1 = results.stream() .filter(purchase -> purchase.getPurchaseId().equals(purchase1.getId())) .toList(); @@ -125,11 +125,11 @@ void getMemberPurchasesFailNotFoundMember() { // given var mockEmail = "test@test.com"; - var command = GetMemberPurchasesCommandDto.builder().memberEmail(mockEmail).build(); + var command = GetMemberAllPurchasesCommandDto.builder().memberEmail(mockEmail).build(); Assertions.assertThatThrownBy(() -> { // when - getMemberPurchasesUseCase.getMemberPurchases(command); + getMemberAllPurchasesUseCase.getMemberPurchases(command); // then }).isInstanceOf(MemberNotFoundException.class); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchaseUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchaseUseCaseTest.java new file mode 100644 index 00000000..9036c591 --- /dev/null +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchaseUseCaseTest.java @@ -0,0 +1,143 @@ +package com.tiketeer.Tiketeer.domain.member.usecase; + +import java.time.LocalDateTime; +import java.util.UUID; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.transaction.annotation.Transactional; + +import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; +import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberPurchaseCommandDto; +import com.tiketeer.Tiketeer.domain.purchase.Purchase; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; +import com.tiketeer.Tiketeer.domain.ticket.Ticket; +import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; +import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; +import com.tiketeer.Tiketeer.testhelper.TestHelper; + +@Import({TestHelper.class}) +@SpringBootTest +public class GetMemberPurchaseUseCaseTest { + @Autowired + private TestHelper testHelper; + + @Autowired + private GetMemberPurchaseUseCase getMemberPurchaseUseCase; + + @Autowired + private TicketingRepository ticketingRepository; + @Autowired + private PurchaseRepository purchaseRepository; + @Autowired + private TicketRepository ticketRepository; + + @Autowired + private PurchaseItemRepository purchaseItemRepository; + + @BeforeEach + void initTable() { + testHelper.initDB(); + } + + @AfterEach + void cleanTable() { + testHelper.cleanDB(); + } + + @Test + @DisplayName("정상 조건 > 멤버 구매 내역 조회 요청 > 성공") + @Transactional + void getMemberPurchases() { + // given + var mockEmail = "test@test.com"; + var member = testHelper.createMember(mockEmail); + var now = LocalDateTime.now(); + var ticketing1 = ticketingRepository.save( + Ticketing.builder() + .member(member) + .description("") + .title("test1") + .location("Seoul") + .eventTime(now) + .category("") + .runningMinutes(600) + .saleStart(now) + .saleEnd(now) + .build() + ); + var ticketing2 = ticketingRepository.save( + Ticketing.builder() + .member(member) + .description("") + .title("test2") + .location("Seoul") + .eventTime(now) + .category("") + .runningMinutes(600) + .saleStart(now) + .saleEnd(now) + .build() + ); + var purchase1 = purchaseRepository.save(new Purchase(member)); + var purchase2 = purchaseRepository.save(new Purchase(member)); + var ticket1 = ticketRepository.save(new Ticket(1000, 4, 4, "ticket1", "", ticketing1)); + var ticket2 = ticketRepository.save(new Ticket(1000, 3, 3, "ticket2", "", ticketing2)); + var ticket3 = ticketRepository.save(new Ticket(1000, 3, 3, "ticket3", "", ticketing2)); + + var purchaseItem1 = purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); + var purchaseItem2 = purchaseItemRepository.save(new PurchaseItem(purchase1, ticket1)); + purchaseItemRepository.save(new PurchaseItem(purchase2, ticket2)); + purchaseItemRepository.save(new PurchaseItem(purchase2, ticket3)); + + // when + var result = getMemberPurchaseUseCase.getMemberPurchases( + GetMemberPurchaseCommandDto.builder().memberEmail(mockEmail).purchaseId(purchase1.getId()).build()); + + // then + Assertions.assertThat(result.getPurchaseId()).isEqualTo(purchase1.getId()); + Assertions.assertThat(result.getTicketingId()).isEqualTo(ticketing1.getId()); + Assertions.assertThat(result.getTitle()).isEqualTo(ticketing1.getTitle()); + Assertions.assertThat(result.getLocation()).isEqualTo(ticketing1.getLocation()); + Assertions.assertThat(result.getEventTime()).isEqualTo(ticketing1.getEventTime()); + Assertions.assertThat(result.getCreatedAt()).isEqualTo(ticketing1.getCreatedAt()); + Assertions.assertThat(result.getRunningMinutes()).isEqualTo(ticketing1.getRunningMinutes()); + Assertions.assertThat(result.getCategory()).isEqualTo(ticketing1.getCategory()); + Assertions.assertThat(result.getThumbnailPath()).isEqualTo(ticketing1.getThumbnailPath()); + Assertions.assertThat(result.getPurchaseItems()) + .extracting("purchaseItemId") + .containsExactlyInAnyOrder(purchaseItem1.getId(), purchaseItem2.getId()); + Assertions.assertThat(result.getPurchaseItems()) + .extracting("description") + .containsExactlyInAnyOrder(purchaseItem1.getTicket().getDescription(), + purchaseItem2.getTicket().getDescription()); + } + + @Test + @DisplayName("멤버가 존재하지 않음 > 멤버 조회 > 실패") + @Transactional(readOnly = true) + void getMemberPurchasesFailNotFoundMember() { + // given + var mockEmail = "test@test.com"; + + var command = GetMemberPurchaseCommandDto.builder() + .memberEmail(mockEmail) + .purchaseId(UUID.randomUUID()) + .build(); + + Assertions.assertThatThrownBy(() -> { + // when + getMemberPurchaseUseCase.getMemberPurchases(command); + // then + }).isInstanceOf(MemberNotFoundException.class); + } +} From 8c6a462da67ea411809f799b769a76f84c874aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B0=B0=ED=9D=99=EA=B3=A0=EC=96=91=EC=9D=B4?= Date: Tue, 11 Jun 2024 20:12:05 +0900 Subject: [PATCH 19/38] ticket crudservice migration --- .../Tiketeer/domain/ticket/Ticket.java | 7 ---- .../controller/MemberControllerTest.java | 8 ++-- .../service/MemberTicketingServiceTest.java | 6 +-- .../GetMemberPurchasesUseCaseTest.java | 8 ++-- .../repository/PurchaseRepositoryTest.java | 8 ++-- .../ticket/service/TicketCrudServiceTest.java | 41 +++++++++++++++---- .../repository/TicketingRepositoryTest.java | 14 ++++--- .../usecase/GetAllTicketingsUseCaseTest.java | 2 +- 8 files changed, 56 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java index 15149c30..e1d4f29c 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/Ticket.java @@ -73,19 +73,12 @@ public class Ticket { private Integer version; @Builder -<<<<<<< HEAD public Ticket(long price, int stock, int remainingStock, String title, String description, Ticketing ticketing) { -======= - public Ticket(long price, int stock, int remainingStock, String title, Ticketing ticketing) { ->>>>>>> 75566d5e58e0182701ef477488ef370d31b8fb50 this.price = price; this.stock = stock; this.remainingStock = remainingStock; this.title = title; -<<<<<<< HEAD this.description = description; -======= ->>>>>>> 75566d5e58e0182701ef477488ef370d31b8fb50 setTicketing(ticketing); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java index 7a568c50..788f4c7e 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java @@ -364,10 +364,10 @@ void getMemberPurchasesSuccess() throws Exception { ); var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing2)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing2)); // when var result = mockMvc.perform(get("/api/members/{memberId}/purchases", member.getId()) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java index 07a31874..462743ab 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/service/MemberTicketingServiceTest.java @@ -69,9 +69,9 @@ void getMemberTicketingSalesSuccess() { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing)); //when var memberTicketingSale = getMemberTicketingSalesUseCase.getMemberTicketingSales( diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java index 55c5a655..90263605 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberPurchasesUseCaseTest.java @@ -84,10 +84,10 @@ void getMemberPurchases() { ); var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing2)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing2)); // when var results = getMemberPurchasesUseCase.getMemberPurchases( diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java index c7046f85..84a0d7f1 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseRepositoryTest.java @@ -74,10 +74,10 @@ void findWithTicketingByMember() { ); var purchase1 = purchaseRepository.save(new Purchase(member)); var purchase2 = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 2", ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 3", ticketing1)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 4", ticketing2)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 2", "description 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 3", "description 1", ticketing1)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 4", "description 1", ticketing2)); // when var results = purchaseRepository.findWithTicketingByMember(member); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java index ccc45e99..2be4e80f 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java @@ -1,7 +1,9 @@ package com.tiketeer.Tiketeer.domain.ticket.service; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.UUID; import org.assertj.core.api.Assertions; @@ -17,6 +19,8 @@ import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketCommandDto; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; @@ -71,7 +75,7 @@ void listTicketByTicketingSuccess() { var mockStock = 30; var ticketing = createTicketing(member, now.plusYears(1), now.plusYears(2), now.plusYears(3)); - createTickets(ticketing, mockStock, mockStock, "", "", 1000); + createTickets(ticketing, 5, mockStock, mockStock, "", "", 1000); // when var tickets = ticketCrudService.listTicketByTicketingId(ticketing.getId()); @@ -88,13 +92,16 @@ void createTicketsFailBecauseNotExistTicketing() { Assertions.assertThatThrownBy(() -> { // when - ticketCrudService.createTickets(invalidTicketingId, 100); + ticketCrudService.createTickets(CreateTicketCommandDto.builder() + .ticketingId(invalidTicketingId) + .createTicketMetadataList(new ArrayList<>()) + .build()); // then }).isInstanceOf(TicketingNotFoundException.class); } @Test - @DisplayName("유효한 티케팅 (기존 티켓 10) > 추가 하위 티켓 생성 요청 (20) > 성공 및 총 재고 10 + 20") + @DisplayName("유효한 티케팅 (티켓 10종류 * 10장) > 추가 하위 티켓 생성 요청 (1종류 20장) > 성공 및 총 재고 10*10 + 20") void createTicketsSuccess() { // given var now = LocalDateTime.now(); @@ -104,16 +111,30 @@ void createTicketsSuccess() { var mockStock = 10; var ticketing = createTicketing(member, now.plusYears(1), now.plusYears(2), now.plusYears(3)); - createTickets(ticketing, mockStock, mockStock, "", "", 1000); + createTickets(ticketing, 10, mockStock, mockStock, "", "", 1000); var addTickets = 20; // when - ticketCrudService.createTickets(ticketingId, addTickets); + ticketCrudService.createTickets(CreateTicketCommandDto.builder() + .ticketingId(ticketing.getId()) + .createTicketMetadataList(Collections.singletonList( + CreateTicketMetadata.builder() + .stock(addTickets) + .remainingStock(addTickets) + .title("") + .description("") + .price(2000) + .build() + )) + .build()); // then - var tickets = ticketCrudService.listTicketByTicketingId(ticketingId); - Assertions.assertThat(tickets.size()).isEqualTo(mockStock + addTickets); + var tickets = ticketCrudService.listTicketByTicketingId(ticketing.getId()); + Assertions.assertThat(tickets.size()).isEqualTo(mockStock + 1); + + var ticketCount = tickets.stream().mapToInt(Ticket::getStock).sum(); + Assertions.assertThat(ticketCount).isEqualTo(120); } private Ticketing createTicketing(Member member, LocalDateTime saleStart, @@ -131,14 +152,16 @@ private Ticketing createTicketing(Member member, LocalDateTime saleStart, } - private void createTickets(Ticketing ticketing, int stock, int remainingStock, String title, String description, + private void createTickets(Ticketing ticketing, int distinct, int stock, int remainingStock, String title, + String description, long price) { - ticketRepository.saveAll(Arrays.stream(new int[stock]) + ticketRepository.saveAll(Arrays.stream(new int[distinct]) .mapToObj(i -> Ticket.builder() .ticketing(ticketing) .price(price) .title(title) .description(description) + .stock(stock) .remainingStock(remainingStock) .build()) .toList()); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java index 99c150bb..5913f3ee 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java @@ -65,9 +65,9 @@ void findTicketingWithTicketStock() { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); - ticketRepository.save(new Ticket(2000, 2, 2, "title 2", ticketing)); - ticketRepository.save(new Ticket(3000, 3, 3, "title 3", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing)); + ticketRepository.save(new Ticket(2000, 2, 2, "title 2", "description 1", ticketing)); + ticketRepository.save(new Ticket(3000, 3, 3, "title 3", "description 1", ticketing)); //when var memberTicketingSale = ticketingRepository.findTicketingWithTicketStock(member.getEmail()).getFirst(); @@ -106,7 +106,8 @@ void findAllTicketingsWithRemainStockSuccess() { ); var stock1 = 3; for (int i = 0; i < stock1; i++) { - ticketRepository.save(new Ticket(1000 * (i + 1), i + 1, i + 1, "title " + i, ticketing1)); + ticketRepository.save( + new Ticket(1000 * (i + 1), i + 1, i + 1, "title " + i, "description " + i, ticketing1)); } var ticketing2 = ticketingRepository.save( Ticketing.builder() @@ -123,7 +124,8 @@ void findAllTicketingsWithRemainStockSuccess() { ); var stock2 = 1; for (int i = 0; i < stock2; i++) { - ticketRepository.save(new Ticket(1000 * (i + 1), i + 1, i + 1, "title " + i, ticketing2)); + ticketRepository.save( + new Ticket(1000 * (i + 1), i + 1, i + 1, "title " + i, "description " + i, ticketing2)); } //when @@ -178,7 +180,7 @@ void findTicketingWithRemainStock() { var purchase = purchaseRepository.save(new Purchase(member)); var stock = 3; for (int i = 0; i < stock; i++) { - ticketRepository.save(new Ticket(1000 * (i + 1), i + 1, i, "title " + i, ticketing)); + ticketRepository.save(new Ticket(1000 * (i + 1), i + 1, i, "title " + i, "description " + i, ticketing)); } //when diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java index f1921938..0d5fae11 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetAllTicketingsUseCaseTest.java @@ -91,7 +91,7 @@ private List createTicketings(Member member, int count) { .build() ).toList(); ticketings.forEach(ticketing -> { - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", ticketing)); + ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing)); }); return ticketingRepository.saveAll(ticketings); } From 34e555b35363878e9742f423f7725002731478fb Mon Sep 17 00:00:00 2001 From: dla0510 Date: Tue, 11 Jun 2024 20:33:05 +0900 Subject: [PATCH 20/38] =?UTF-8?q?TicketStockService=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/NotEnoughTicketException.java | 10 ----- .../usecase/CreatePurchaseUseCaseCore.java | 17 +++---- .../InvalidRemainingStockException.java | 10 +++++ .../NotEnoughRemainingStockException.java | 10 +++++ .../ticket/service/TicketStockService.java | 44 +++++++++++++++++++ .../exception/code/PurchaseExceptionCode.java | 3 +- .../exception/code/TicketExceptionCode.java | 4 +- .../Tiketeer/configuration/AppConfig.java | 9 ++-- .../domain/purchase/PurchaseTestHelper.java | 4 +- .../usecase/CreatePurchaseUseCaseTest.java | 4 +- 10 files changed, 82 insertions(+), 33 deletions(-) delete mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/exception/NotEnoughTicketException.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/ticket/exception/InvalidRemainingStockException.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/ticket/exception/NotEnoughRemainingStockException.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketStockService.java diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/exception/NotEnoughTicketException.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/exception/NotEnoughTicketException.java deleted file mode 100644 index 0e9dde7b..00000000 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/exception/NotEnoughTicketException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.tiketeer.Tiketeer.domain.purchase.exception; - -import com.tiketeer.Tiketeer.exception.DefinedException; -import com.tiketeer.Tiketeer.exception.code.PurchaseExceptionCode; - -public class NotEnoughTicketException extends DefinedException { - public NotEnoughTicketException() { - super(PurchaseExceptionCode.NOT_ENOUGH_TICKET); - } -} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java index 4830c7fb..0cf2658b 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java @@ -13,7 +13,6 @@ import com.tiketeer.Tiketeer.domain.member.service.MemberPointService; import com.tiketeer.Tiketeer.domain.purchase.Purchase; import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; -import com.tiketeer.Tiketeer.domain.purchase.exception.NotEnoughTicketException; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.purchase.service.PurchaseService; @@ -21,35 +20,35 @@ import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseResultDto; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingService; +import com.tiketeer.Tiketeer.domain.ticket.service.TicketStockService; @Service public class CreatePurchaseUseCaseCore implements CreatePurchaseUseCase { private final PurchaseRepository purchaseRepository; private final PurchaseItemRepository purchaseItemRepository; - private final TicketingService ticketingService; private final PurchaseService purchaseService; private final MemberPointService memberPointService; private final MemberCrudService memberCrudService; private final TicketRepository ticketRepository; + private final TicketStockService ticketStockService; @Autowired public CreatePurchaseUseCaseCore( PurchaseRepository purchaseRepository, PurchaseItemRepository purchaseItemRepository, - TicketingService ticketingService, PurchaseService purchaseService, MemberPointService memberPointService, MemberCrudService memberCrudService, - TicketRepository ticketRepository + TicketRepository ticketRepository, + TicketStockService ticketStockService ) { this.purchaseRepository = purchaseRepository; this.purchaseItemRepository = purchaseItemRepository; - this.ticketingService = ticketingService; this.purchaseService = purchaseService; this.memberPointService = memberPointService; this.memberCrudService = memberCrudService; this.ticketRepository = ticketRepository; + this.ticketStockService = ticketStockService; } @Override @@ -84,14 +83,10 @@ private CreatePurchaseResultDto execPurchaseLogic(CreatePurchaseCommandDto comma for (var t : command.getTickets()) { var ticket = ticketMap.get(t.getId()); - var stock = ticket.getRemainingStock(); var purchaseCount = t.getCount(); - if (stock < purchaseCount) { - throw new NotEnoughTicketException(); - } + ticketStockService.subtractRemainingStock(t.getId(), purchaseCount); var purchaseItems = Stream.generate(() -> new PurchaseItem(purchase, ticket)).limit(purchaseCount).toList(); purchaseItemRepository.saveAll(purchaseItems); - ticket.setRemainingStock(stock - purchaseCount); } var totalCost = command.getTickets() diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/exception/InvalidRemainingStockException.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/exception/InvalidRemainingStockException.java new file mode 100644 index 00000000..002bca14 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/exception/InvalidRemainingStockException.java @@ -0,0 +1,10 @@ +package com.tiketeer.Tiketeer.domain.ticket.exception; + +import com.tiketeer.Tiketeer.exception.DefinedException; +import com.tiketeer.Tiketeer.exception.code.TicketExceptionCode; + +public class InvalidRemainingStockException extends DefinedException { + public InvalidRemainingStockException() { + super(TicketExceptionCode.INVALID_REMAINING_STOCK); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/exception/NotEnoughRemainingStockException.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/exception/NotEnoughRemainingStockException.java new file mode 100644 index 00000000..02fe5e06 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/exception/NotEnoughRemainingStockException.java @@ -0,0 +1,10 @@ +package com.tiketeer.Tiketeer.domain.ticket.exception; + +import com.tiketeer.Tiketeer.exception.DefinedException; +import com.tiketeer.Tiketeer.exception.code.TicketExceptionCode; + +public class NotEnoughRemainingStockException extends DefinedException { + public NotEnoughRemainingStockException() { + super(TicketExceptionCode.NOT_ENOUGH_REMAINING_STOCK); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketStockService.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketStockService.java new file mode 100644 index 00000000..0a36e44f --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketStockService.java @@ -0,0 +1,44 @@ +package com.tiketeer.Tiketeer.domain.ticket.service; + +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.tiketeer.Tiketeer.domain.ticket.exception.InvalidRemainingStockException; +import com.tiketeer.Tiketeer.domain.ticket.exception.NotEnoughRemainingStockException; +import com.tiketeer.Tiketeer.domain.ticket.exception.TicketNotFoundException; +import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class TicketStockService { + private final TicketRepository ticketRepository; + + @Autowired + public TicketStockService(TicketRepository ticketRepository) { + this.ticketRepository = ticketRepository; + } + + @Transactional + public void subtractRemainingStock(UUID ticketId, int amount) { + var ticket = ticketRepository.findById(ticketId).orElseThrow(TicketNotFoundException::new); + if (ticket.getRemainingStock() < amount) { + throw new NotEnoughRemainingStockException(); + } + ticket.setRemainingStock(ticket.getRemainingStock() - amount); + } + + @Transactional + public void addRemainingStock(UUID ticketId, int amount) { + var ticket = ticketRepository.findById(ticketId).orElseThrow(TicketNotFoundException::new); + if (ticket.getStock() < ticket.getRemainingStock() + amount) { + throw new InvalidRemainingStockException(); + } + ticket.setRemainingStock(ticket.getRemainingStock() + amount); + } + +} diff --git a/src/main/java/com/tiketeer/Tiketeer/exception/code/PurchaseExceptionCode.java b/src/main/java/com/tiketeer/Tiketeer/exception/code/PurchaseExceptionCode.java index 2cee55c7..10dbe7ce 100644 --- a/src/main/java/com/tiketeer/Tiketeer/exception/code/PurchaseExceptionCode.java +++ b/src/main/java/com/tiketeer/Tiketeer/exception/code/PurchaseExceptionCode.java @@ -12,8 +12,7 @@ public enum PurchaseExceptionCode implements ExceptionCode { EMPTY_PURCHASE(HttpStatus.CONFLICT, "구매 내역에 티켓이 존재하지 않습니다."), EMPTY_PURCHASE_TOKEN(HttpStatus.UNAUTHORIZED, "헤더에 구매 대기열 토큰이 존재하지 않습니다."), PURCHASE_NOT_IN_SALE_PERIOD(HttpStatus.BAD_REQUEST, "티켓 판매 기간이 아닙니다."), - - NOT_ENOUGH_TICKET(HttpStatus.CONFLICT, "구매 가능한 티켓이 부족합니다."), + ACCESS_FOR_NOT_OWNED_PURCHASE(HttpStatus.FORBIDDEN, "본인 소유가 아닌 구매 내역에 접근 불가능합니다."), PURCHASE_TOKEN_NOT_VALID(HttpStatus.UNAUTHORIZED, "구매 대기열 토큰이 유효하지 않습니다."), User_NOT_IN_ORDER(HttpStatus.FORBIDDEN, "구매 가능한 대기열 순위에 아직 들지 않았습니다."), diff --git a/src/main/java/com/tiketeer/Tiketeer/exception/code/TicketExceptionCode.java b/src/main/java/com/tiketeer/Tiketeer/exception/code/TicketExceptionCode.java index 32858dd2..d91b9be1 100644 --- a/src/main/java/com/tiketeer/Tiketeer/exception/code/TicketExceptionCode.java +++ b/src/main/java/com/tiketeer/Tiketeer/exception/code/TicketExceptionCode.java @@ -8,9 +8,11 @@ @Getter @RequiredArgsConstructor public enum TicketExceptionCode implements ExceptionCode { + NOT_ENOUGH_REMAINING_STOCK(HttpStatus.BAD_REQUEST, "티켓 재고가 부족합니다."), + INVALID_REMAINING_STOCK(HttpStatus.BAD_REQUEST, "유효하지 않은 수량입니다"), TICKET_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 티켓입니다."), TICKET_CONCURRENCY(HttpStatus.CONFLICT, "티켓 조회에 실패했습니다."); - + private final HttpStatus httpStatus; private final String message; } diff --git a/src/test/java/com/tiketeer/Tiketeer/configuration/AppConfig.java b/src/test/java/com/tiketeer/Tiketeer/configuration/AppConfig.java index 2101b451..574db381 100644 --- a/src/test/java/com/tiketeer/Tiketeer/configuration/AppConfig.java +++ b/src/test/java/com/tiketeer/Tiketeer/configuration/AppConfig.java @@ -12,7 +12,7 @@ import com.tiketeer.Tiketeer.domain.purchase.usecase.CreatePurchaseUseCase; import com.tiketeer.Tiketeer.domain.purchase.usecase.CreatePurchaseUseCaseCore; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingService; +import com.tiketeer.Tiketeer.domain.ticket.service.TicketStockService; @Configuration public class AppConfig { @@ -20,12 +20,11 @@ public class AppConfig { @Profile("test") public CreatePurchaseUseCase createPurchaseUseCase(PurchaseRepository purchaseRepository, PurchaseItemRepository purchaseItemRepository, - TicketingService ticketingService, PurchaseService purchaseService, MemberPointService memberPointService, - MemberCrudService memberCrudService, TicketRepository ticketRepository) { - return new CreatePurchaseUseCaseCore(purchaseRepository, purchaseItemRepository, ticketingService, + MemberCrudService memberCrudService, TicketRepository ticketRepository, TicketStockService ticketStockService) { + return new CreatePurchaseUseCaseCore(purchaseRepository, purchaseItemRepository, purchaseService, - memberPointService, memberCrudService, ticketRepository); + memberPointService, memberCrudService, ticketRepository, ticketStockService); } } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseTestHelper.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseTestHelper.java index 04204974..4950654a 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseTestHelper.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/PurchaseTestHelper.java @@ -11,10 +11,10 @@ import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; -import com.tiketeer.Tiketeer.domain.purchase.exception.NotEnoughTicketException; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.ticket.Ticket; +import com.tiketeer.Tiketeer.domain.ticket.exception.NotEnoughRemainingStockException; import com.tiketeer.Tiketeer.domain.ticket.exception.TicketNotFoundException; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; @@ -48,7 +48,7 @@ public Pair> createPurchase(UUID memberId, UUID tic private List createPurchaseItems(Purchase purchase, Ticket ticket, int count) { var stock = ticket.getRemainingStock(); if (stock < count) { - throw new NotEnoughTicketException(); + throw new NotEnoughRemainingStockException(); } ticket.setRemainingStock(stock - count); var purchaseItems = Stream.generate(() -> new PurchaseItem(purchase, ticket)).limit(count).toList(); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java index bd6343ee..b2c6b9ee 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java @@ -15,13 +15,13 @@ import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; import com.tiketeer.Tiketeer.domain.member.exception.NotEnoughPointException; -import com.tiketeer.Tiketeer.domain.purchase.exception.NotEnoughTicketException; import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotInSalePeriodException; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseCommandDto; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; import com.tiketeer.Tiketeer.domain.ticket.Ticket; +import com.tiketeer.Tiketeer.domain.ticket.exception.NotEnoughRemainingStockException; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; @@ -168,7 +168,7 @@ void createPurchaseFailNotEnoughTicket() { // when createPurchaseUseCase.createPurchase(createPurchaseCommand); // then - }).isInstanceOf(NotEnoughTicketException.class); + }).isInstanceOf(NotEnoughRemainingStockException.class); } @Test From 9f774281ed5e9a2d8777fe30988b399999890681 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Tue, 11 Jun 2024 21:03:56 +0900 Subject: [PATCH 21/38] =?UTF-8?q?TicketStockService=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/TicketStockServiceTest.java | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketStockServiceTest.java diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketStockServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketStockServiceTest.java new file mode 100644 index 00000000..c27e2c28 --- /dev/null +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketStockServiceTest.java @@ -0,0 +1,113 @@ +package com.tiketeer.Tiketeer.domain.ticket.service; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.transaction.annotation.Transactional; + +import com.tiketeer.Tiketeer.domain.ticket.exception.InvalidRemainingStockException; +import com.tiketeer.Tiketeer.domain.ticket.exception.NotEnoughRemainingStockException; +import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; +import com.tiketeer.Tiketeer.testhelper.TestHelper; + +@Import({TestHelper.class, TicketingTestHelper.class}) +@SpringBootTest +@DisplayName("TicketStockService 테스트") +public class TicketStockServiceTest { + @Autowired + private TestHelper testHelper; + @Autowired + private TicketingTestHelper ticketingTestHelper; + @Autowired + private TicketStockService ticketStockService; + @Autowired + private TicketRepository ticketRepository; + + @BeforeEach + void initTable() { + testHelper.initDB(); + } + + @AfterEach + void cleanTable() { + testHelper.cleanDB(); + } + + @Test + @DisplayName("티켓 재고 충분 > 티켓 재고 감소 요청 > 성공") + @Transactional + void subtractRemainingStockSuccess() { + // given + var mockEmail = "test@test.com"; + var member = testHelper.createMember(mockEmail); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, 2); + var ticketId = ticketRepository.findAll().getFirst().getId(); + + // when + ticketStockService.subtractRemainingStock(ticketId, 1); + + // then + var ticketInDb = ticketRepository.findById(ticketId).orElseThrow(); + Assertions.assertThat(ticketInDb.getRemainingStock()).isEqualTo(1); + } + + @Test + @DisplayName("티켓 재고 부족 > 티켓 재고 감소 요청 > 실패") + @Transactional + void subtractRemainingStockFailNotEnoughRemainingStock() { + // given + var mockEmail = "test@test.com"; + var member = testHelper.createMember(mockEmail); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, 2); + var ticketId = ticketRepository.findAll().getFirst().getId(); + + Assertions.assertThatThrownBy(() -> { + // when + ticketStockService.subtractRemainingStock(ticketId, 3); + // then + }).isInstanceOf(NotEnoughRemainingStockException.class); + } + + @Test + @DisplayName("티켓 재고 부족 > 티켓 재고 증가 요청 > 성공") + @Transactional + void addRemainingStockSuccess() { + // given + var mockEmail = "test@test.com"; + var member = testHelper.createMember(mockEmail); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, 2); + var ticket = ticketRepository.findAll().getFirst(); + ticket.setRemainingStock(0); + + // when + ticketStockService.addRemainingStock(ticket.getId(), 1); + + // then + var ticketInDb = ticketRepository.findById(ticket.getId()).orElseThrow(); + Assertions.assertThat(ticketInDb.getRemainingStock()).isEqualTo(1); + } + + @Test + @DisplayName("티켓 재고 충분 > 티켓 재고 증가 요청 > 실패") + @Transactional + void addRemainingStockFailInvalidRemainingStock() { + // given + var mockEmail = "test@test.com"; + var member = testHelper.createMember(mockEmail); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, 2); + var ticketId = ticketRepository.findAll().getFirst().getId(); + + Assertions.assertThatThrownBy(() -> { + // when + ticketStockService.addRemainingStock(ticketId, 1); + // then + }).isInstanceOf(InvalidRemainingStockException.class); + } + +} From 5bf67fd845a44d8d78ad16dbd6cb42579bd54388 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Tue, 11 Jun 2024 21:17:14 +0900 Subject: [PATCH 22/38] =?UTF-8?q?DeletePurchaseItems=20EP=20=EB=A7=88?= =?UTF-8?q?=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PurchaseController.java | 18 +- .../dto/DeletePurchaseItemsRequestDto.java | 32 +++ .../dto/DeletePurchaseTicketsRequestDto.java | 34 --- .../repository/PurchaseItemRepository.java | 3 + .../usecase/DeletePurchaseItemsUseCase.java | 73 +++++ .../usecase/DeletePurchaseTicketsUseCase.java | 45 --- ...ava => DeletePurchaseItemsCommandDto.java} | 8 +- .../controller/PurchaseControllerTest.java | 42 +-- .../PurchaseItemRepositoryTest.java | 89 ++++++ .../DeletePurchaseItemsUseCaseTest.java | 208 ++++++++++++++ .../DeletePurchaseTicketsUseCaseTest.java | 267 ------------------ 11 files changed, 440 insertions(+), 379 deletions(-) create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/DeletePurchaseItemsRequestDto.java delete mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/DeletePurchaseTicketsRequestDto.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseItemsUseCase.java delete mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCase.java rename src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/{DeletePurchaseTicketsCommandDto.java => DeletePurchaseItemsCommandDto.java} (72%) create mode 100644 src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepositoryTest.java create mode 100644 src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseItemsUseCaseTest.java delete mode 100644 src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCaseTest.java diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseController.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseController.java index facfb7ef..0e45699c 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseController.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseController.java @@ -15,11 +15,11 @@ import com.tiketeer.Tiketeer.auth.SecurityContextHelper; import com.tiketeer.Tiketeer.domain.purchase.annotation.CheckWaitingQueueAndToken; -import com.tiketeer.Tiketeer.domain.purchase.controller.dto.DeletePurchaseTicketsRequestDto; +import com.tiketeer.Tiketeer.domain.purchase.controller.dto.DeletePurchaseItemsRequestDto; import com.tiketeer.Tiketeer.domain.purchase.controller.dto.PostPurchaseRequestDto; import com.tiketeer.Tiketeer.domain.purchase.controller.dto.PostPurchaseResponseDto; import com.tiketeer.Tiketeer.domain.purchase.usecase.CreatePurchaseUseCase; -import com.tiketeer.Tiketeer.domain.purchase.usecase.DeletePurchaseTicketsUseCase; +import com.tiketeer.Tiketeer.domain.purchase.usecase.DeletePurchaseItemsUseCase; import com.tiketeer.Tiketeer.domain.purchase.usecase.GetPurchaseTicketsUseCase; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.GetPurchaseTicketsCommandDto; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.GetPurchaseTicketsResponseDto; @@ -31,16 +31,16 @@ @RequestMapping("/purchases") public class PurchaseController { private final CreatePurchaseUseCase createPurchaseUseCase; - private final DeletePurchaseTicketsUseCase deletePurchaseTicketsUseCase; + private final DeletePurchaseItemsUseCase deletePurchaseItemsUseCase; private final GetPurchaseTicketsUseCase getPurchaseTicketsUseCase; private final SecurityContextHelper securityContextHelper; @Autowired PurchaseController(CreatePurchaseUseCase createPurchaseUseCase, - DeletePurchaseTicketsUseCase deletePurchaseTicketsUseCase, SecurityContextHelper securityContextHelper, + DeletePurchaseItemsUseCase deletePurchaseItemsUseCase, SecurityContextHelper securityContextHelper, GetPurchaseTicketsUseCase getPurchaseTicketsUseCase) { this.createPurchaseUseCase = createPurchaseUseCase; - this.deletePurchaseTicketsUseCase = deletePurchaseTicketsUseCase; + this.deletePurchaseItemsUseCase = deletePurchaseItemsUseCase; this.getPurchaseTicketsUseCase = getPurchaseTicketsUseCase; this.securityContextHelper = securityContextHelper; } @@ -55,11 +55,11 @@ public ResponseEntity> postPurchase( return ResponseEntity.status(HttpStatus.CREATED).body(responseBody); } - @DeleteMapping("/{purchaseId}/tickets") - public ResponseEntity deletePurchaseTickets(@PathVariable UUID purchaseId, - @Valid @RequestBody DeletePurchaseTicketsRequestDto request) { + @DeleteMapping("/{purchaseId}/items") + public ResponseEntity deletePurchaseItems(@PathVariable UUID purchaseId, + @Valid @RequestBody DeletePurchaseItemsRequestDto request) { var memberEmail = securityContextHelper.getEmailInToken(); - deletePurchaseTicketsUseCase.deletePurchaseTickets(request.convertToDto(memberEmail, purchaseId)); + deletePurchaseItemsUseCase.deletePurchaseItems(request.convertToDto(memberEmail, purchaseId)); return ResponseEntity.status(HttpStatus.OK).build(); } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/DeletePurchaseItemsRequestDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/DeletePurchaseItemsRequestDto.java new file mode 100644 index 00000000..e3a4b259 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/DeletePurchaseItemsRequestDto.java @@ -0,0 +1,32 @@ +package com.tiketeer.Tiketeer.domain.purchase.controller.dto; + +import java.util.List; +import java.util.UUID; + +import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.DeletePurchaseItemsCommandDto; + +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@ToString +@NoArgsConstructor(force = true) +public class DeletePurchaseItemsRequestDto { + @NotNull + private final List purchaseItemIds; + + @Builder + public DeletePurchaseItemsRequestDto(@NotNull List purchaseItemIds) { + this.purchaseItemIds = purchaseItemIds; + } + + public DeletePurchaseItemsCommandDto convertToDto(String memberEmail, UUID purchaseId) { + return DeletePurchaseItemsCommandDto.builder().memberEmail(memberEmail) + .purchaseItemIds(purchaseItemIds) + .purchaseId(purchaseId) + .build(); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/DeletePurchaseTicketsRequestDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/DeletePurchaseTicketsRequestDto.java deleted file mode 100644 index a0c80f5d..00000000 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/DeletePurchaseTicketsRequestDto.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.tiketeer.Tiketeer.domain.purchase.controller.dto; - -import java.util.List; -import java.util.UUID; - -import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.DeletePurchaseTicketsCommandDto; - -import jakarta.validation.constraints.NotNull; -import lombok.Builder; -import lombok.Getter; -import lombok.ToString; - -@Getter -@ToString -public class DeletePurchaseTicketsRequestDto { - @NotNull - private final List ticketIds; - - @NotNull - private final UUID purchaseId; - - @Builder - public DeletePurchaseTicketsRequestDto(@NotNull List ticketIds, @NotNull UUID purchaseId) { - this.ticketIds = ticketIds; - this.purchaseId = purchaseId; - } - - public DeletePurchaseTicketsCommandDto convertToDto(String memberEmail, UUID purchaseId) { - return DeletePurchaseTicketsCommandDto.builder().memberEmail(memberEmail) - .ticketIds(this.ticketIds) - .purchaseId(purchaseId) - .build(); - } -} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepository.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepository.java index cc09832d..78cffb9c 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepository.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepository.java @@ -1,12 +1,15 @@ package com.tiketeer.Tiketeer.domain.purchase.repository; +import java.util.List; import java.util.UUID; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import com.tiketeer.Tiketeer.domain.purchase.Purchase; import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; @Repository public interface PurchaseItemRepository extends JpaRepository { + List findAllByPurchase(Purchase purchase); } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseItemsUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseItemsUseCase.java new file mode 100644 index 00000000..eb8bf263 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseItemsUseCase.java @@ -0,0 +1,73 @@ +package com.tiketeer.Tiketeer.domain.purchase.usecase; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.tiketeer.Tiketeer.domain.member.service.MemberCrudService; +import com.tiketeer.Tiketeer.domain.member.service.MemberPointService; +import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotFoundException; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; +import com.tiketeer.Tiketeer.domain.purchase.service.PurchaseService; +import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.DeletePurchaseItemsCommandDto; +import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.service.TicketStockService; + +@Service +public class DeletePurchaseItemsUseCase { + private final PurchaseRepository purchaseRepository; + private final PurchaseItemRepository purchaseItemRepository; + private final TicketRepository ticketRepository; + private final PurchaseService purchaseService; + private final MemberPointService memberPointService; + private final MemberCrudService memberCrudService; + private final TicketStockService ticketStockService; + + @Autowired + DeletePurchaseItemsUseCase(PurchaseRepository purchaseRepository, PurchaseItemRepository purchaseItemRepository, + TicketRepository ticketRepository, PurchaseService purchaseService, MemberPointService memberPointService, + MemberCrudService memberCrudService, TicketStockService ticketStockService) { + this.purchaseRepository = purchaseRepository; + this.purchaseItemRepository = purchaseItemRepository; + this.ticketRepository = ticketRepository; + this.purchaseService = purchaseService; + this.memberPointService = memberPointService; + this.memberCrudService = memberCrudService; + this.ticketStockService = ticketStockService; + } + + @Transactional + public void deletePurchaseItems(DeletePurchaseItemsCommandDto command) { + var purchase = purchaseRepository.findById(command.getPurchaseId()).orElseThrow( + PurchaseNotFoundException::new); + purchaseService.validatePurchaseOwnership(purchase.getId(), command.getMemberEmail()); + + var purchaseItemsToRefund = purchaseItemRepository.findAllById(command.getPurchaseItemIds()); + + purchaseService.validateTicketingSalePeriod( + purchaseItemsToRefund.getFirst().getTicket().getTicketing().getId(), LocalDateTime.now()); + + Map ticketMap = new HashMap<>(command.getPurchaseItemIds().size()); + for (var pi : purchaseItemsToRefund) { + ticketMap.merge(pi.getTicket().getId(), 1, Integer::sum); + } + for (Map.Entry entry : ticketMap.entrySet()) { + var ticketId = entry.getKey(); + var count = entry.getValue(); + ticketStockService.addRemainingStock(ticketId, count); + } + purchaseItemRepository.deleteAllInBatch(purchaseItemsToRefund); + if (purchaseItemRepository.findAllByPurchase(purchase).isEmpty()) { + purchaseRepository.delete(purchase); + } + var member = memberCrudService.findByEmail(command.getMemberEmail()); + var totalCost = purchaseItemsToRefund.stream().mapToLong(x -> x.getTicket().getPrice()).sum(); + memberPointService.addPoint(member.getId(), totalCost); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCase.java deleted file mode 100644 index ad8973de..00000000 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCase.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.tiketeer.Tiketeer.domain.purchase.usecase; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import com.tiketeer.Tiketeer.domain.member.service.MemberCrudService; -import com.tiketeer.Tiketeer.domain.member.service.MemberPointService; -import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotFoundException; -import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; -import com.tiketeer.Tiketeer.domain.purchase.service.PurchaseService; -import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.DeletePurchaseTicketsCommandDto; -import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; - -@Service -public class DeletePurchaseTicketsUseCase { - private final PurchaseRepository purchaseRepository; - private final TicketRepository ticketRepository; - private final PurchaseService purchaseService; - private final MemberPointService memberPointService; - private final MemberCrudService memberCrudService; - - @Autowired - DeletePurchaseTicketsUseCase(PurchaseRepository purchaseRepository, - TicketRepository ticketRepository, PurchaseService purchaseService, MemberPointService memberPointService, - MemberCrudService memberCrudService) { - this.purchaseRepository = purchaseRepository; - this.ticketRepository = ticketRepository; - this.purchaseService = purchaseService; - this.memberPointService = memberPointService; - this.memberCrudService = memberCrudService; - } - - @Transactional - public void deletePurchaseTickets(DeletePurchaseTicketsCommandDto command) { - var purchase = purchaseRepository.findById(command.getPurchaseId()).orElseThrow( - PurchaseNotFoundException::new); - var ticketsToRefund = ticketRepository.findAllById(command.getTicketIds()); - - purchaseService.validatePurchaseOwnership(purchase.getId(), command.getMemberEmail()); - - var member = memberCrudService.findByEmail(command.getMemberEmail()); - memberPointService.addPoint(member.getId(), ticketsToRefund.getFirst().getPrice()); - } -} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/DeletePurchaseTicketsCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/DeletePurchaseItemsCommandDto.java similarity index 72% rename from src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/DeletePurchaseTicketsCommandDto.java rename to src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/DeletePurchaseItemsCommandDto.java index 51f56c10..f5922b6b 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/DeletePurchaseTicketsCommandDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/DeletePurchaseItemsCommandDto.java @@ -10,18 +10,18 @@ @Getter @ToString -public class DeletePurchaseTicketsCommandDto { +public class DeletePurchaseItemsCommandDto { private final String memberEmail; - private final List ticketIds; + private final List purchaseItemIds; private final UUID purchaseId; private LocalDateTime commandCreatedAt = LocalDateTime.now(); @Builder - public DeletePurchaseTicketsCommandDto(String memberEmail, List ticketIds, + public DeletePurchaseItemsCommandDto(String memberEmail, List purchaseItemIds, UUID purchaseId, LocalDateTime commandCreatedAt) { this.memberEmail = memberEmail; - this.ticketIds = ticketIds; + this.purchaseItemIds = purchaseItemIds; this.purchaseId = purchaseId; if (commandCreatedAt != null) { this.commandCreatedAt = commandCreatedAt; diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseControllerTest.java index 6bc65fd1..7556f526 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseControllerTest.java @@ -6,7 +6,6 @@ import java.nio.file.Path; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.UUID; @@ -31,8 +30,9 @@ import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; import com.tiketeer.Tiketeer.domain.purchase.PurchaseTestHelper; -import com.tiketeer.Tiketeer.domain.purchase.controller.dto.DeletePurchaseTicketsRequestDto; +import com.tiketeer.Tiketeer.domain.purchase.controller.dto.DeletePurchaseItemsRequestDto; import com.tiketeer.Tiketeer.domain.purchase.controller.dto.PostPurchaseRequestDto; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseCommandDto; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.GetPurchaseTicketsResponseDto; @@ -73,6 +73,8 @@ public class PurchaseControllerTest { private RedisTemplate redisTemplate; @Autowired private RedisTestHelper redisTestHelper; + @Autowired + private PurchaseItemRepository purchaseItemRepository; @TempDir static Path tempDir; @@ -344,18 +346,17 @@ void deletePurchaseTicketsSuccess() throws Exception { var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketRepository.findAll().getFirst().getId(), 2); var purchase = purchaseTicketPair.getFirst(); - var tickets = purchaseTicketPair.getSecond(); + var purchaseItems = purchaseTicketPair.getSecond(); - List ticketsToRefund = Collections.singletonList(tickets.getFirst().getId()); - DeletePurchaseTicketsRequestDto req = DeletePurchaseTicketsRequestDto.builder() - .purchaseId(purchase.getId()) - .ticketIds(ticketsToRefund) + List purchaseItemsToRefund = List.of(purchaseItems.getFirst().getId()); + DeletePurchaseItemsRequestDto req = DeletePurchaseItemsRequestDto.builder() + .purchaseItemIds(purchaseItemsToRefund) .build(); String content = objectMapper.writeValueAsString(req); // when mockMvc - .perform(delete("/api/purchases/{purchaseId}/tickets", purchase.getId()) + .perform(delete("/api/purchases/{purchaseId}/items", purchase.getId()) .contextPath("/api") .contentType(MediaType.APPLICATION_JSON) .characterEncoding("utf-8") @@ -364,7 +365,7 @@ void deletePurchaseTicketsSuccess() throws Exception { ) //then .andExpect(status().isOk()); - assertThat(ticketRepository.findAll().size()).isEqualTo(1); + assertThat(purchaseItemRepository.findAll().size()).isEqualTo(1); } @Test @@ -376,20 +377,20 @@ void deletePurchaseAllTicketsSuccess() throws Exception { Member member = memberRepository.findAll().getFirst(); Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, 1000, 2); - var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketing.getId(), 2); + var ticketId = ticketRepository.findAll().getFirst().getId(); + var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketId, 2); var purchase = purchaseTicketPair.getFirst(); - var tickets = purchaseTicketPair.getSecond(); + var purchaseItems = purchaseTicketPair.getSecond(); - List ticketsToRefund = tickets.stream().map(PurchaseItem::getId).toList(); - DeletePurchaseTicketsRequestDto req = DeletePurchaseTicketsRequestDto.builder() - .purchaseId(purchase.getId()) - .ticketIds(ticketsToRefund) + List purchaseItemsToRefund = purchaseItems.stream().map(PurchaseItem::getId).toList(); + DeletePurchaseItemsRequestDto req = DeletePurchaseItemsRequestDto.builder() + .purchaseItemIds(purchaseItemsToRefund) .build(); String content = objectMapper.writeValueAsString(req); // when mockMvc - .perform(delete("/api/purchases/{purchaseId}/tickets", purchase.getId()) + .perform(delete("/api/purchases/{purchaseId}/items", purchase.getId()) .contextPath("/api") .contentType(MediaType.APPLICATION_JSON) .characterEncoding("utf-8") @@ -399,7 +400,7 @@ void deletePurchaseAllTicketsSuccess() throws Exception { //then .andExpect(status().isOk()); assertThat(purchaseRepository.findById(purchase.getId())).isEmpty(); - assertThat(ticketRepository.findAll().size()).isEqualTo(0); + assertThat(purchaseItemRepository.findAll().size()).isEqualTo(0); } @Test @@ -411,9 +412,10 @@ void getPurchaseTicketsSuccess() throws Exception { Member member = memberRepository.findAll().getFirst(); Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); var ticketing = ticketingTestHelper.createTicketing(member.getId(), 1, 1000, 4); - var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketing.getId(), 2); + var ticketId = ticketRepository.findAll().getFirst().getId(); + var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketId, 2); var purchase = purchaseTicketPair.getFirst(); - var tickets = purchaseTicketPair.getSecond(); + var purchaseItems = purchaseTicketPair.getSecond(); // when var result = mockMvc @@ -433,7 +435,7 @@ void getPurchaseTicketsSuccess() throws Exception { var ticketIds = response.getData().getTicketIds(); assertThat(ticketIds.size()).isEqualTo(2); - tickets.forEach(ticket -> { + purchaseItems.forEach(ticket -> { assertThat(ticketIds.contains(ticket.getId())).isTrue(); }); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepositoryTest.java new file mode 100644 index 00000000..acc45935 --- /dev/null +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/repository/PurchaseItemRepositoryTest.java @@ -0,0 +1,89 @@ +package com.tiketeer.Tiketeer.domain.purchase.repository; + +import java.time.LocalDateTime; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.transaction.annotation.Transactional; + +import com.tiketeer.Tiketeer.domain.member.Member; +import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; +import com.tiketeer.Tiketeer.domain.purchase.Purchase; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; +import com.tiketeer.Tiketeer.domain.role.Role; +import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; +import com.tiketeer.Tiketeer.domain.role.repository.RoleRepository; +import com.tiketeer.Tiketeer.domain.ticket.Ticket; +import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; +import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@DataJpaTest +public class PurchaseItemRepositoryTest { + @Autowired + private TicketRepository ticketRepository; + + @Autowired + private PurchaseRepository purchaseRepository; + + @Autowired + private TicketingRepository ticketingRepository; + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private RoleRepository roleRepository; + + @Autowired + private PurchaseItemRepository purchaseItemRepository; + + @Test + @DisplayName("멤버 구매 내역 생성 > 멤버 구매 내역 + 티켓 정보와 함께 조회 > 성공") + @Transactional + void findAllByPurchase() throws Throwable { + var mockEmail = "test@test.com"; + var role = roleRepository.save(new Role(RoleEnum.SELLER)); + var member = memberRepository.save( + new Member(mockEmail, "asdf1234", 0L, false, null, role)); + var now = LocalDateTime.now(); + var ticketing1 = ticketingRepository.save( + Ticketing.builder() + .member(member) + .description("") + .title("test1") + .location("Seoul") + .eventTime(now) + .category("") + .runningMinutes(600) + .saleStart(now) + .saleEnd(now) + .build() + ); + + var ticket1 = ticketRepository.save(new Ticket(1000, 3, 3, "ticket1", "", ticketing1)); + + var purchase = purchaseRepository.save(new Purchase(member)); + + var purchaseItem1 = purchaseItemRepository.save(new PurchaseItem(purchase, ticket1)); + var purchaseItem2 = purchaseItemRepository.save(new PurchaseItem(purchase, ticket1)); + + // when + var purchaseItems = purchaseItemRepository.findAllByPurchase(purchase); + + // then + Assertions.assertThat(purchaseItems.size()).isEqualTo(2); + Assertions.assertThat(purchaseItems) + .extracting("id") + .containsExactlyInAnyOrder(purchaseItem1.getId(), purchaseItem2.getId()); + Assertions.assertThat(purchaseItems.getFirst().getTicket().getId()).isEqualTo(ticket1.getId()); + + } + +} diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseItemsUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseItemsUseCaseTest.java new file mode 100644 index 00000000..6b55fcd4 --- /dev/null +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseItemsUseCaseTest.java @@ -0,0 +1,208 @@ +package com.tiketeer.Tiketeer.domain.purchase.usecase; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.transaction.annotation.Transactional; + +import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseTestHelper; +import com.tiketeer.Tiketeer.domain.purchase.exception.AccessForNotOwnedPurchaseException; +import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotFoundException; +import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotInSalePeriodException; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; +import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.DeletePurchaseItemsCommandDto; +import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; +import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; +import com.tiketeer.Tiketeer.testhelper.TestHelper; + +@Import({TestHelper.class, TicketingTestHelper.class, PurchaseTestHelper.class}) +@SpringBootTest +public class DeletePurchaseItemsUseCaseTest { + @Autowired + private TestHelper testHelper; + @Autowired + private TicketingTestHelper ticketingTestHelper; + @Autowired + private PurchaseTestHelper purchaseTestHelper; + @Autowired + private DeletePurchaseItemsUseCase deletePurchaseItemsUseCase; + @Autowired + private PurchaseRepository purchaseRepository; + @Autowired + private TicketRepository ticketRepository; + @Autowired + private TicketingRepository ticketingRepository; + @Autowired + private MemberRepository memberRepository; + @Autowired + private PurchaseItemRepository purchaseItemRepository; + + @BeforeEach + void initTable() { + testHelper.initDB(); + } + + @AfterEach + void cleanTable() { + testHelper.cleanDB(); + } + + @Test + @DisplayName("구매 내역 일부 환불 > 티켓 환불 요청 > 성공") + @Transactional + void deletePurchaseTicketsSuccess() { + // given + var mockEmail = "test1@test.com"; + var member = testHelper.createMember(mockEmail, "1q2w3e4r!!"); + var price = 1000; + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, price, 5); + var ticketId = ticketRepository.findAll().getFirst().getId(); + var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketId, 2); + var purchase = purchaseTicketPair.getFirst(); + var purchaseItems = purchaseTicketPair.getSecond(); + + List purchaseItemsToRefund = List.of(purchaseItems.getFirst().getId()); + var deletePurchaseCommand = DeletePurchaseItemsCommandDto.builder() + .memberEmail(mockEmail) + .purchaseId(purchase.getId()) + .purchaseItemIds(purchaseItemsToRefund) + .build(); + + // when + deletePurchaseItemsUseCase.deletePurchaseItems(deletePurchaseCommand); + + // then + var purchaseInDbOpt = purchaseRepository.findById(purchase.getId()); + Assertions.assertThat(purchaseInDbOpt.isPresent()).isTrue(); + + var purchaseItemsInDb = purchaseItemRepository.findAllByPurchase(purchase); + Assertions.assertThat(purchaseItemsInDb.size()).isEqualTo(1); + + var memberInDB = memberRepository.findByEmail(mockEmail).orElseThrow(); + Assertions.assertThat(memberInDB.getPoint()).isEqualTo(price); + } + + @Test + @DisplayName("구매 내역 전체 환불 > 티켓 환불 요청 > 성공") + @Transactional + void deletePurchaseAllTicketsSuccess() { + // given + var mockEmail = "test1@test.com"; + var member = testHelper.createMember(mockEmail, "1234"); + var price = 1000; + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, price, 5); + var ticketId = ticketRepository.findAll().getFirst().getId(); + var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketId, 2); + var purchase = purchaseTicketPair.getFirst(); + var purchaseItems = purchaseTicketPair.getSecond(); + + List purchaseItemsToRefund = purchaseItems.stream().map(PurchaseItem::getId).toList(); + var deletePurchaseCommand = DeletePurchaseItemsCommandDto.builder() + .memberEmail(mockEmail) + .purchaseId(purchase.getId()) + .purchaseItemIds(purchaseItemsToRefund) + .build(); + + // when + deletePurchaseItemsUseCase.deletePurchaseItems(deletePurchaseCommand); + + // then + var purchaseInDbOpt = purchaseRepository.findById(purchase.getId()); + Assertions.assertThat(purchaseInDbOpt.isPresent()).isFalse(); + + var purchaseItemsInDb = purchaseItemRepository.findAllByPurchase(purchase); + Assertions.assertThat(purchaseItemsInDb.size()).isEqualTo(0); + + var memberInDB = memberRepository.findByEmail(mockEmail).orElseThrow(); + Assertions.assertThat(memberInDB.getPoint()).isEqualTo(price * purchaseItemsToRefund.size()); + } + + @Test + @DisplayName("구매 내역이 존재하지 않음 > 티켓 환불 요청 > 실패") + @Transactional + void deletePurchaseTicketsFailPurchaseNotFound() { + // given + var mockEmail = "test1@test.com"; + testHelper.createMember(mockEmail, "1234"); + + List purchaseItemsToRefund = List.of(UUID.randomUUID()); + var deletePurchaseCommand = DeletePurchaseItemsCommandDto.builder() + .memberEmail(mockEmail) + .purchaseId(UUID.randomUUID()) + .purchaseItemIds(purchaseItemsToRefund) + .build(); + + Assertions.assertThatThrownBy(() -> { + // when + deletePurchaseItemsUseCase.deletePurchaseItems(deletePurchaseCommand); + // then + }).isInstanceOf(PurchaseNotFoundException.class); + } + + @Test + @DisplayName("구매 내역 소유자가 아님 > 티켓 환불 요청 > 실패") + @Transactional + void deletePurchaseTicketsFailNotPurchaseOwner() { + // given + var mockEmail = "test1@test.com"; + var member = testHelper.createMember(mockEmail, "1234"); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, 5); + var ticketId = ticketRepository.findAll().getFirst().getId(); + var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketId, 1); + var purchase = purchaseTicketPair.getFirst(); + var purchaseItems = purchaseTicketPair.getSecond(); + + List purchaseItemsToRefund = List.of(purchaseItems.getFirst().getId()); + var deletePurchaseCommand = DeletePurchaseItemsCommandDto.builder() + .memberEmail("another@test.com") + .purchaseId(purchase.getId()) + .purchaseItemIds(purchaseItemsToRefund) + .build(); + + Assertions.assertThatThrownBy(() -> { + // when + deletePurchaseItemsUseCase.deletePurchaseItems(deletePurchaseCommand); + // then + }).isInstanceOf(AccessForNotOwnedPurchaseException.class); + } + + @Test + @DisplayName("티켓팅 판매 기간이 아님 > 티켓 환불 요청 > 실패") + @Transactional + void deletePurchaseTicketsFailNotTicketingSalePeriod() { + // given + var mockEmail = "test1@test.com"; + var member = testHelper.createMember(mockEmail, "1234"); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), -2, 1); + var ticketId = ticketRepository.findAll().getFirst().getId(); + var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketId, 1); + var purchase = purchaseTicketPair.getFirst(); + var purchaseItems = purchaseTicketPair.getSecond(); + + List purchaseItemsToRefund = Collections.singletonList(purchaseItems.getFirst().getId()); + var deletePurchaseCommand = DeletePurchaseItemsCommandDto.builder() + .memberEmail(mockEmail) + .purchaseId(purchase.getId()) + .purchaseItemIds(purchaseItemsToRefund) + .build(); + + Assertions.assertThatThrownBy(() -> { + // when + deletePurchaseItemsUseCase.deletePurchaseItems(deletePurchaseCommand); + // then + }).isInstanceOf(PurchaseNotInSalePeriodException.class); + } +} diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCaseTest.java deleted file mode 100644 index c63cdedb..00000000 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/DeletePurchaseTicketsUseCaseTest.java +++ /dev/null @@ -1,267 +0,0 @@ -package com.tiketeer.Tiketeer.domain.purchase.usecase; - -import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.UUID; - -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; -import org.springframework.data.domain.Limit; -import org.springframework.data.util.Pair; -import org.springframework.transaction.annotation.Transactional; - -import com.tiketeer.Tiketeer.domain.member.Member; -import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; -import com.tiketeer.Tiketeer.domain.purchase.Purchase; -import com.tiketeer.Tiketeer.domain.purchase.exception.AccessForNotOwnedPurchaseException; -import com.tiketeer.Tiketeer.domain.purchase.exception.EmptyPurchaseException; -import com.tiketeer.Tiketeer.domain.purchase.exception.NotEnoughTicketException; -import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotFoundException; -import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotInSalePeriodException; -import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; -import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.DeletePurchaseTicketsCommandDto; -import com.tiketeer.Tiketeer.domain.ticket.Ticket; -import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; -import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; -import com.tiketeer.Tiketeer.testhelper.TestHelper; - -@Import({TestHelper.class}) -@SpringBootTest -public class DeletePurchaseTicketsUseCaseTest { - @Autowired - private TestHelper testHelper; - @Autowired - private DeletePurchaseTicketsUseCase deletePurchaseTicketsUseCase; - @Autowired - private PurchaseRepository purchaseRepository; - @Autowired - private TicketRepository ticketRepository; - @Autowired - private TicketingRepository ticketingRepository; - @Autowired - private MemberRepository memberRepository; - - @BeforeEach - void initTable() { - testHelper.initDB(); - } - - @AfterEach - void cleanTable() { - testHelper.cleanDB(); - } - - @Test - @DisplayName("구매 내역 일부 환불 > 티켓 환불 요청 > 성공") - @Transactional - void deletePurchaseTicketsSuccess() { - // given - var mockEmail = "test1@test.com"; - var member = testHelper.createMember(mockEmail, "1q2w3e4r!!"); - var ticketing = createTicketing(member, 0, 5); - var purchaseTicketPair = createPurchase(member, ticketing, 2); - var purchase = purchaseTicketPair.getFirst(); - var tickets = purchaseTicketPair.getSecond(); - - List ticketsToRefund = Collections.singletonList(tickets.getFirst().getId()); - var deletePurchaseCommand = DeletePurchaseTicketsCommandDto.builder() - .memberEmail(mockEmail) - .purchaseId(purchase.getId()) - .ticketIds(ticketsToRefund) - .build(); - - // when - deletePurchaseTicketsUseCase.deletePurchaseTickets(deletePurchaseCommand); - - // then - var purchaseInDbOpt = purchaseRepository.findById(purchase.getId()); - Assertions.assertThat(purchaseInDbOpt.isPresent()).isTrue(); - - var ticketsUnderPurchase = ticketRepository.findAll(); - Assertions.assertThat(ticketsUnderPurchase.size()).isEqualTo(1); - } - - @Test - @DisplayName("구매 내역 전체 환불 > 티켓 환불 요청 > 성공") - @Transactional - void deletePurchaseAllTicketsSuccess() { - // given - var mockEmail = "test1@test.com"; - var member = testHelper.createMember(mockEmail, "1234"); - var ticketing = createTicketing(member, 0, 5); - var purchaseTicketPair = createPurchase(member, ticketing, 2); - var purchase = purchaseTicketPair.getFirst(); - var tickets = purchaseTicketPair.getSecond(); - - List ticketsToRefund = tickets.stream().map(Ticket::getId).toList(); - var deletePurchaseCommand = DeletePurchaseTicketsCommandDto.builder() - .memberEmail(mockEmail) - .purchaseId(purchase.getId()) - .ticketIds(ticketsToRefund) - .build(); - - // when - deletePurchaseTicketsUseCase.deletePurchaseTickets(deletePurchaseCommand); - - // then - var purchaseInDbOpt = purchaseRepository.findById(purchase.getId()); - Assertions.assertThat(purchaseInDbOpt.isPresent()).isFalse(); - - var ticketsUnderPurchase = ticketRepository.findAll(); - Assertions.assertThat(ticketsUnderPurchase.size()).isEqualTo(0); - - var memberInDB = memberRepository.findByEmail(mockEmail).orElseThrow(); - Assertions.assertThat(member.getPoint()).isEqualTo(1000L * ticketsToRefund.size()); - } - - @Test - @DisplayName("구매 내역이 존재하지 않음 > 티켓 환불 요청 > 실패") - @Transactional - void deletePurchaseTicketsFailPurchaseNotFound() { - // given - var mockEmail = "test1@test.com"; - testHelper.createMember(mockEmail, "1234"); - - List ticketsToRefund = Collections.singletonList(UUID.randomUUID()); - var deletePurchaseCommand = DeletePurchaseTicketsCommandDto.builder() - .memberEmail(mockEmail) - .purchaseId(UUID.randomUUID()) - .ticketIds(ticketsToRefund) - .build(); - - Assertions.assertThatThrownBy(() -> { - // when - deletePurchaseTicketsUseCase.deletePurchaseTickets(deletePurchaseCommand); - // then - }).isInstanceOf(PurchaseNotFoundException.class); - } - - @Test - @DisplayName("빈 구매 내역 > 티켓 환불 요청 > 실패") - @Transactional - void deletePurchaseTicketsFailEmptyPurchase() { - // given - var mockEmail = "test1@test.com"; - var member = testHelper.createMember(mockEmail, "1234"); - var ticketing = createTicketing(member, 0, 5); - var purchaseTicketPair = createPurchase(member, ticketing, 0); - var purchase = purchaseTicketPair.getFirst(); - - List ticketsToRefund = Collections.singletonList(UUID.randomUUID()); - var deletePurchaseCommand = DeletePurchaseTicketsCommandDto.builder() - .memberEmail(mockEmail) - .purchaseId(purchase.getId()) - .ticketIds(ticketsToRefund) - .build(); - - Assertions.assertThatThrownBy(() -> { - // when - deletePurchaseTicketsUseCase.deletePurchaseTickets(deletePurchaseCommand); - // then - }).isInstanceOf(EmptyPurchaseException.class); - } - - @Test - @DisplayName("구매 내역 소유자가 아님 > 티켓 환불 요청 > 실패") - @Transactional - void deletePurchaseTicketsFailNotPurchaseOwner() { - // given - var mockEmail = "test1@test.com"; - var member = testHelper.createMember(mockEmail, "1234"); - var ticketing = createTicketing(member, 0, 5); - var purchaseTicketPair = createPurchase(member, ticketing, 1); - var purchase = purchaseTicketPair.getFirst(); - var tickets = purchaseTicketPair.getSecond(); - - List ticketsToRefund = Collections.singletonList(tickets.getFirst().getId()); - var deletePurchaseCommand = DeletePurchaseTicketsCommandDto.builder() - .memberEmail("another@test.com") - .purchaseId(purchase.getId()) - .ticketIds(ticketsToRefund) - .build(); - - Assertions.assertThatThrownBy(() -> { - // when - deletePurchaseTicketsUseCase.deletePurchaseTickets(deletePurchaseCommand); - // then - }).isInstanceOf(AccessForNotOwnedPurchaseException.class); - } - - @Test - @DisplayName("티켓팅 판매 기간이 아님 > 티켓 환불 요청 > 실패") - @Transactional - void deletePurchaseTicketsFailNotTicketingSalePeriod() { - // given - var mockEmail = "test1@test.com"; - var member = testHelper.createMember(mockEmail, "1234"); - var ticketing = createTicketing(member, -2, 1); - var purchaseTicketPair = createPurchase(member, ticketing, 1); - var purchase = purchaseTicketPair.getFirst(); - var tickets = purchaseTicketPair.getSecond(); - - List ticketsToRefund = Collections.singletonList(tickets.getFirst().getId()); - var deletePurchaseCommand = DeletePurchaseTicketsCommandDto.builder() - .memberEmail(mockEmail) - .purchaseId(purchase.getId()) - .ticketIds(ticketsToRefund) - .build(); - - Assertions.assertThatThrownBy(() -> { - // when - deletePurchaseTicketsUseCase.deletePurchaseTickets(deletePurchaseCommand); - // then - }).isInstanceOf(PurchaseNotInSalePeriodException.class); - } - - private Ticketing createTicketing(Member member, int saleStartAfterYears, int stock) { - var now = LocalDateTime.now(); - var eventTime = now.plusYears(saleStartAfterYears + 2); - var saleStart = now.plusYears(saleStartAfterYears); - var saleEnd = now.plusYears(saleStartAfterYears + 1); - var ticketing = ticketingRepository.save(Ticketing.builder() - .title("test") - .member(member) - .description("") - .location("Seoul") - .eventTime(eventTime) - .saleStart(saleStart) - .saleEnd(saleEnd) - .category("concert") - .runningMinutes(300).build()); - ticketRepository.saveAll(Arrays.stream(new int[stock]) - .mapToObj(i -> Ticket.builder().ticketing(ticketing).build()) - .toList()); - return ticketing; - } - - private Pair> createPurchase(Member member, Ticketing ticketing, int count) { - var purchase = this.purchaseRepository.save(Purchase.builder().member(member).build()); - - if (count > 0) { - var tickets = updateTicketPurchase(purchase, ticketing, count); - return Pair.of(purchase, tickets); - } - return Pair.of(purchase, Collections.emptyList()); - } - - private List updateTicketPurchase(Purchase purchase, Ticketing ticketing, int count) { - var tickets = this.ticketRepository.findByTicketingIdOrderById(ticketing.getId(), - Limit.of(count)); - if (tickets.size() < count) { - throw new NotEnoughTicketException(); - } - tickets.forEach(ticket -> { - this.ticketRepository.save(ticket); - }); - return tickets; - } -} From e76374507008b43ca3ff36298c2313210d088298 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Tue, 11 Jun 2024 21:19:51 +0900 Subject: [PATCH 23/38] =?UTF-8?q?GetPurchaseTickets=20EP=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PurchaseController.java | 22 +--- .../usecase/GetPurchaseTicketsUseCase.java | 40 ------- .../dto/GetPurchaseTicketsCommandDto.java | 20 ---- .../dto/GetPurchaseTicketsResponseDto.java | 28 ----- .../dto/GetPurchaseTicketsResultDto.java | 20 ---- .../controller/PurchaseControllerTest.java | 40 ------- .../GetPurchaseTicketsUseCaseTest.java | 103 ------------------ 7 files changed, 1 insertion(+), 272 deletions(-) delete mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/GetPurchaseTicketsUseCase.java delete mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/GetPurchaseTicketsCommandDto.java delete mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/GetPurchaseTicketsResponseDto.java delete mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/GetPurchaseTicketsResultDto.java delete mode 100644 src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/GetPurchaseTicketsUseCaseTest.java diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseController.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseController.java index 0e45699c..12122579 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseController.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseController.java @@ -6,7 +6,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -20,9 +19,6 @@ import com.tiketeer.Tiketeer.domain.purchase.controller.dto.PostPurchaseResponseDto; import com.tiketeer.Tiketeer.domain.purchase.usecase.CreatePurchaseUseCase; import com.tiketeer.Tiketeer.domain.purchase.usecase.DeletePurchaseItemsUseCase; -import com.tiketeer.Tiketeer.domain.purchase.usecase.GetPurchaseTicketsUseCase; -import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.GetPurchaseTicketsCommandDto; -import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.GetPurchaseTicketsResponseDto; import com.tiketeer.Tiketeer.response.ApiResponse; import jakarta.validation.Valid; @@ -32,16 +28,13 @@ public class PurchaseController { private final CreatePurchaseUseCase createPurchaseUseCase; private final DeletePurchaseItemsUseCase deletePurchaseItemsUseCase; - private final GetPurchaseTicketsUseCase getPurchaseTicketsUseCase; private final SecurityContextHelper securityContextHelper; @Autowired PurchaseController(CreatePurchaseUseCase createPurchaseUseCase, - DeletePurchaseItemsUseCase deletePurchaseItemsUseCase, SecurityContextHelper securityContextHelper, - GetPurchaseTicketsUseCase getPurchaseTicketsUseCase) { + DeletePurchaseItemsUseCase deletePurchaseItemsUseCase, SecurityContextHelper securityContextHelper) { this.createPurchaseUseCase = createPurchaseUseCase; this.deletePurchaseItemsUseCase = deletePurchaseItemsUseCase; - this.getPurchaseTicketsUseCase = getPurchaseTicketsUseCase; this.securityContextHelper = securityContextHelper; } @@ -62,17 +55,4 @@ public ResponseEntity deletePurchaseItems(@PathVariable UUID purchaseId, deletePurchaseItemsUseCase.deletePurchaseItems(request.convertToDto(memberEmail, purchaseId)); return ResponseEntity.status(HttpStatus.OK).build(); } - - @GetMapping("/{purchaseId}/tickets") - public ResponseEntity> getPurchaseTickets( - @PathVariable UUID purchaseId) { - - var memberEmail = securityContextHelper.getEmailInToken(); - var result = getPurchaseTicketsUseCase.getPurchaseTickets( - new GetPurchaseTicketsCommandDto(purchaseId, memberEmail)); - var responseBody = ApiResponse.wrap(GetPurchaseTicketsResponseDto.convertFromDto(result)); - - return ResponseEntity.status(HttpStatus.OK).body(responseBody); - } - } \ No newline at end of file diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/GetPurchaseTicketsUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/GetPurchaseTicketsUseCase.java deleted file mode 100644 index 3b8bbcf9..00000000 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/GetPurchaseTicketsUseCase.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.tiketeer.Tiketeer.domain.purchase.usecase; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotFoundException; -import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; -import com.tiketeer.Tiketeer.domain.purchase.service.PurchaseService; -import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.GetPurchaseTicketsCommandDto; -import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.GetPurchaseTicketsResultDto; -import com.tiketeer.Tiketeer.domain.ticket.Ticket; -import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; - -@Service -public class GetPurchaseTicketsUseCase { - private final PurchaseRepository purchaseRepository; - private final TicketRepository ticketRepository; - private final PurchaseService purchaseService; - - @Autowired - GetPurchaseTicketsUseCase(PurchaseRepository purchaseRepository, - TicketRepository ticketRepository, PurchaseService purchaseService) { - this.purchaseRepository = purchaseRepository; - this.ticketRepository = ticketRepository; - this.purchaseService = purchaseService; - } - - public GetPurchaseTicketsResultDto getPurchaseTickets(GetPurchaseTicketsCommandDto command) { - - purchaseService.validatePurchaseOwnership(command.getPurchaseId(), command.getMemberEmail()); - - var purchase = purchaseRepository.findById(command.getPurchaseId()).orElseThrow(PurchaseNotFoundException::new); - var tickets = ticketRepository.findAll(); - - return GetPurchaseTicketsResultDto.builder() - .ticketIds(tickets.stream().map(Ticket::getId).toList()) - .build(); - } - -} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/GetPurchaseTicketsCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/GetPurchaseTicketsCommandDto.java deleted file mode 100644 index 6dfac4e6..00000000 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/GetPurchaseTicketsCommandDto.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.tiketeer.Tiketeer.domain.purchase.usecase.dto; - -import java.util.UUID; - -import lombok.Builder; -import lombok.Getter; -import lombok.ToString; - -@Getter -@ToString -public class GetPurchaseTicketsCommandDto { - private final UUID purchaseId; - private final String memberEmail; - - @Builder - public GetPurchaseTicketsCommandDto(UUID purchaseId, String memberEmail) { - this.purchaseId = purchaseId; - this.memberEmail = memberEmail; - } -} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/GetPurchaseTicketsResponseDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/GetPurchaseTicketsResponseDto.java deleted file mode 100644 index f15ca963..00000000 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/GetPurchaseTicketsResponseDto.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.tiketeer.Tiketeer.domain.purchase.usecase.dto; - -import java.util.List; -import java.util.UUID; - -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@Getter -@ToString -@NoArgsConstructor(force = true) -public class GetPurchaseTicketsResponseDto { - - private final List ticketIds; - - @Builder - public GetPurchaseTicketsResponseDto(List ticketIds) { - this.ticketIds = ticketIds; - } - - public static GetPurchaseTicketsResponseDto convertFromDto(GetPurchaseTicketsResultDto dto) { - return GetPurchaseTicketsResponseDto.builder() - .ticketIds(dto.getTicketIds()) - .build(); - } -} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/GetPurchaseTicketsResultDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/GetPurchaseTicketsResultDto.java deleted file mode 100644 index 86f70442..00000000 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/dto/GetPurchaseTicketsResultDto.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.tiketeer.Tiketeer.domain.purchase.usecase.dto; - -import java.util.List; -import java.util.UUID; - -import lombok.Builder; -import lombok.Getter; -import lombok.ToString; - -@Getter -@ToString -public class GetPurchaseTicketsResultDto { - - private final List ticketIds; - - @Builder - public GetPurchaseTicketsResultDto(List ticketIds) { - this.ticketIds = ticketIds; - } -} diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseControllerTest.java index 7556f526..38f45994 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/controller/PurchaseControllerTest.java @@ -35,12 +35,10 @@ import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseCommandDto; -import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.GetPurchaseTicketsResponseDto; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; import com.tiketeer.Tiketeer.infra.redis.RedisService; -import com.tiketeer.Tiketeer.response.ApiResponse; import com.tiketeer.Tiketeer.testhelper.RedisTestHelper; import com.tiketeer.Tiketeer.testhelper.TestHelper; @@ -402,42 +400,4 @@ void deletePurchaseAllTicketsSuccess() throws Exception { assertThat(purchaseRepository.findById(purchase.getId())).isEmpty(); assertThat(purchaseItemRepository.findAll().size()).isEqualTo(0); } - - @Test - @DisplayName("티켓팅 및 티켓 생성 및 구매 > 구매 하위 티켓 목록 조회 > 성공") - @Transactional - void getPurchaseTicketsSuccess() throws Exception { - //given - String token = testHelper.registerAndLoginAndReturnAccessToken("user@example.com", RoleEnum.SELLER); - Member member = memberRepository.findAll().getFirst(); - Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); - var ticketing = ticketingTestHelper.createTicketing(member.getId(), 1, 1000, 4); - var ticketId = ticketRepository.findAll().getFirst().getId(); - var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketId, 2); - var purchase = purchaseTicketPair.getFirst(); - var purchaseItems = purchaseTicketPair.getSecond(); - - // when - var result = mockMvc - .perform(get("/api/purchases/{purchaseId}/tickets", purchase.getId()) - .contextPath("/api") - .contentType(MediaType.APPLICATION_JSON) - .characterEncoding("utf-8") - .cookie(cookie) - ) - //then - .andExpect(status().isOk()); - //then - - ApiResponse response = testHelper.getDeserializedApiResponse( - result.andReturn().getResponse().getContentAsString(), - GetPurchaseTicketsResponseDto.class); - - var ticketIds = response.getData().getTicketIds(); - assertThat(ticketIds.size()).isEqualTo(2); - purchaseItems.forEach(ticket -> { - assertThat(ticketIds.contains(ticket.getId())).isTrue(); - }); - } - } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/GetPurchaseTicketsUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/GetPurchaseTicketsUseCaseTest.java deleted file mode 100644 index 7167d4a0..00000000 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/GetPurchaseTicketsUseCaseTest.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.tiketeer.Tiketeer.domain.purchase.usecase; - -import static org.assertj.core.api.Assertions.*; - -import java.util.UUID; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; -import org.springframework.transaction.annotation.Transactional; - -import com.tiketeer.Tiketeer.domain.purchase.PurchaseTestHelper; -import com.tiketeer.Tiketeer.domain.purchase.exception.AccessForNotOwnedPurchaseException; -import com.tiketeer.Tiketeer.domain.purchase.exception.PurchaseNotFoundException; -import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.GetPurchaseTicketsCommandDto; -import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; -import com.tiketeer.Tiketeer.testhelper.TestHelper; - -@Import({TestHelper.class, TicketingTestHelper.class, PurchaseTestHelper.class}) -@SpringBootTest -class GetPurchaseTicketsUseCaseTest { - @Autowired - private TestHelper testHelper; - @Autowired - private TicketingTestHelper ticketingTestHelper; - @Autowired - private PurchaseTestHelper purchaseTestHelper; - @Autowired - private GetPurchaseTicketsUseCase getPurchaseTicketsUseCase; - - @BeforeEach - void initTable() { - testHelper.initDB(); - } - - @AfterEach - void cleanTable() { - testHelper.cleanDB(); - } - - @Test - @DisplayName("티켓 구매 > 하나의 구매 하위 티켓 목록 조회 > 성공") - @Transactional - void getPurchaseTicketsSuccess() { - //given - var email = "test@test.com"; - var member = testHelper.createMember(email, "1q2w3e4r!!"); - var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, 10); - var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketing.getId(), 5); - var purchase = purchaseTicketPair.getFirst(); - var tickets = purchaseTicketPair.getSecond(); - //when - var ticketIds = getPurchaseTicketsUseCase.getPurchaseTickets( - new GetPurchaseTicketsCommandDto(purchase.getId(), email)).getTicketIds(); - - //then - assertThat(ticketIds.size()).isEqualTo(5); - tickets.forEach(ticket -> { - assertThat(ticketIds.contains(ticket.getId())).isTrue(); - }); - - } - - @Test - @DisplayName("존재하지 않는 구매 > 하위 티켓 목록 조회 > 실패") - @Transactional - void getPurchaseTicketsFailNoSuchPurchase() { - //given - var email = "test@test.com"; - testHelper.createMember(email, "1q2w3e4r!!"); - - assertThatThrownBy(() -> { - //when - getPurchaseTicketsUseCase.getPurchaseTickets(new GetPurchaseTicketsCommandDto(UUID.randomUUID(), email)); - //then - }).isInstanceOf(PurchaseNotFoundException.class); - - } - - @Test - @DisplayName("구매 내역 소유자가 아님 > 티켓 조회 요청 > 실패") - @Transactional - void getPurchaseTicketsFailNotPurchaseOwner() { - //given - var email = "test@test.com"; - var member = testHelper.createMember(email, "1q2w3e4r!!"); - var ticketing = ticketingTestHelper.createTicketing(member.getId(), 0, 10); - var purchaseTicketPair = purchaseTestHelper.createPurchase(member.getId(), ticketing.getId(), 5); - var purchase = purchaseTicketPair.getFirst(); - - assertThatThrownBy(() -> { - //when - getPurchaseTicketsUseCase.getPurchaseTickets( - new GetPurchaseTicketsCommandDto(purchase.getId(), "malory@test.com")); - //then - }).isInstanceOf(AccessForNotOwnedPurchaseException.class); - - } -} \ No newline at end of file From 27e8467ae0a1e8aa928499ce70a8902d10e698f3 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Thu, 13 Jun 2024 14:48:08 +0900 Subject: [PATCH 24/38] =?UTF-8?q?GetMemberPurchaseResponseDto=20thumbnailP?= =?UTF-8?q?ath=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/dto/GetMemberPurchaseResponseDto.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchaseResponseDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchaseResponseDto.java index 31039e75..12681708 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchaseResponseDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/controller/dto/GetMemberPurchaseResponseDto.java @@ -21,12 +21,13 @@ public final class GetMemberPurchaseResponseDto { private final LocalDateTime createdAt; private final String category; private final int runningMinute; + private final String thumbnailPath; private final List purchaseItems; @Builder public GetMemberPurchaseResponseDto(UUID purchaseId, UUID ticketingId, String title, String location, LocalDateTime eventTime, LocalDateTime createdAt, String category, - int runningMinute, + int runningMinute, String thumbnailPath, List purchaseItems) { this.purchaseId = purchaseId; this.ticketingId = ticketingId; @@ -36,6 +37,7 @@ public GetMemberPurchaseResponseDto(UUID purchaseId, UUID ticketingId, String ti this.createdAt = createdAt; this.category = category; this.runningMinute = runningMinute; + this.thumbnailPath = thumbnailPath; this.purchaseItems = purchaseItems; } @@ -49,6 +51,7 @@ public static GetMemberPurchaseResponseDto convertFromDto(GetMemberPurchaseResul .createdAt(dto.getCreatedAt()) .category(dto.getCategory()) .runningMinute(dto.getRunningMinutes()) + .thumbnailPath(dto.getThumbnailPath()) .purchaseItems(dto.getPurchaseItems()) .build(); } From aca75e9f62450493bc24518c8685c762d9800231 Mon Sep 17 00:00:00 2001 From: punkryn Date: Thu, 13 Jun 2024 14:56:17 +0900 Subject: [PATCH 25/38] =?UTF-8?q?ticketing=20=EC=83=9D=EC=84=B1,=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20EP=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/GetMemberTicketingSalesResultDto.java | 8 ++-- .../ticket/service/TicketCrudService.java | 11 +++++ .../dto/PatchTicketingRequestDto.java | 12 ----- .../dto/PostTicketingRequestDto.java | 24 ++++------ .../service/TicketingStockService.java | 19 +++----- .../usecase/CreateTicketingUseCase.java | 14 ++---- .../usecase/UpdateTicketingUseCase.java | 6 +-- .../dto/CreateTicketingCommandDto.java | 14 +++--- .../dto/UpdateTicketingCommandDto.java | 9 ++-- .../configuration/EmbeddedRedisConfig.java | 6 ++- .../CreatePurchaseConcurrencyTest.java | 5 +- ...UseCaseDistributedLockConcurrencyTest.java | 44 ++++++----------- ...eUseCaseOptimisticLockConcurrencyTest.java | 30 +++++------- ...UseCasePessimisticLockConcurrencyTest.java | 24 ++++------ .../controller/TicketingControllerTest.java | 41 ++++++++-------- .../service/TicketingStockServiceTest.java | 47 +++++++++++++------ .../usecase/CreateTicketingUseCaseTest.java | 43 +++++++++++++---- .../usecase/DeleteTicketingUseCaseTest.java | 10 ++-- .../usecase/UpdateTicketingUseCaseTest.java | 26 +++------- .../Tiketeer/infra/redis/RedisCacheTest.java | 11 +++-- .../Tiketeer/testhelper/TestHelperTest.java | 2 +- 21 files changed, 193 insertions(+), 213 deletions(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberTicketingSalesResultDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberTicketingSalesResultDto.java index c0d29871..4ecd6762 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberTicketingSalesResultDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberTicketingSalesResultDto.java @@ -19,8 +19,8 @@ public class GetMemberTicketingSalesResultDto { private final LocalDateTime eventTime; private final LocalDateTime saleStart; private final LocalDateTime saleEnd; - private final long stock; - private final long remainStock; + private final int stock; + private final int remainStock; private final LocalDateTime createdAt; private final String category; private final int runningMinutes; @@ -35,8 +35,8 @@ public GetMemberTicketingSalesResultDto( LocalDateTime eventTime, LocalDateTime saleStart, LocalDateTime saleEnd, - long stock, - long remainStock, + int stock, + int remainStock, LocalDateTime createdAt, String category, int runningMinutes, diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java index 221a273a..568db141 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java @@ -10,6 +10,7 @@ import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketCommandDto; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; @@ -57,4 +58,14 @@ public void deleteAllByTicketIds(List ticketIds) { private Ticketing findTicketingById(UUID ticketingId) { return ticketingRepository.findById(ticketingId).orElseThrow(TicketingNotFoundException::new); } + + @Transactional + public void updateTickets(UUID ticketingId, List tickets) { + var ticketing = findTicketingById(ticketingId); + + ticketRepository.findAllByTicketing(ticketing) + .forEach(ticketRepository::delete); + + createTickets(new CreateTicketCommandDto(ticketingId, tickets)); + } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PatchTicketingRequestDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PatchTicketingRequestDto.java index 54ee11e2..785054a4 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PatchTicketingRequestDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PatchTicketingRequestDto.java @@ -31,12 +31,6 @@ public class PatchTicketingRequestDto { @NotNull private final Integer runningMinutes; - @NotNull - private final Integer stock; - - @NotNull - private final Long price; - @NotNull @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") private final LocalDateTime eventTime; @@ -56,8 +50,6 @@ public PatchTicketingRequestDto( @NotBlank String location, @NotBlank String category, @NotNull Integer runningMinutes, - @NotNull Integer stock, - @NotNull Long price, @NotNull LocalDateTime eventTime, @NotNull LocalDateTime saleStart, @NotNull LocalDateTime saleEnd) { @@ -66,8 +58,6 @@ public PatchTicketingRequestDto( this.location = location; this.category = category; this.runningMinutes = runningMinutes; - this.stock = stock; - this.price = price; this.eventTime = eventTime; this.saleStart = saleStart; this.saleEnd = saleEnd; @@ -83,8 +73,6 @@ public UpdateTicketingCommandDto convertToDto(String ticketingId, String memberE .location(location) .category(category) .runningMinutes(runningMinutes) - .stock(stock) - .price(price) .eventTime(eventTime) .saleStart(saleStart) .saleEnd(saleEnd) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PostTicketingRequestDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PostTicketingRequestDto.java index 9ddfe9c8..57e325bd 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PostTicketingRequestDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PostTicketingRequestDto.java @@ -1,10 +1,12 @@ package com.tiketeer.Tiketeer.domain.ticketing.controller.dto; import java.time.LocalDateTime; +import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.fasterxml.jackson.annotation.JsonFormat; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.CreateTicketingCommandDto; import jakarta.validation.constraints.NotBlank; @@ -32,14 +34,6 @@ public class PostTicketingRequestDto { @Positive private final Integer runningMinutes; - @NotNull - @Positive - private final Integer stock; - - @NotNull - @Positive - private final Long price; - @NotNull @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") private final LocalDateTime eventTime; @@ -52,6 +46,9 @@ public class PostTicketingRequestDto { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") private final LocalDateTime saleEnd; + @NotNull + private final List tickets; + @Builder public PostTicketingRequestDto( @NotBlank String title, @@ -59,21 +56,19 @@ public PostTicketingRequestDto( @NotBlank String location, @NotBlank String category, @NotNull Integer runningMinutes, - @NotNull Integer stock, - @NotNull Long price, @NotNull LocalDateTime eventTime, @NotNull LocalDateTime saleStart, - @NotNull LocalDateTime saleEnd) { + @NotNull LocalDateTime saleEnd, + @NotNull List tickets) { this.title = title; this.description = description; this.location = location; this.category = category; this.runningMinutes = runningMinutes; - this.stock = stock; - this.price = price; this.eventTime = eventTime; this.saleStart = saleStart; this.saleEnd = saleEnd; + this.tickets = tickets; } public CreateTicketingCommandDto convertToDto(String memberEmail, MultipartFile thumbnail) { @@ -85,11 +80,10 @@ public CreateTicketingCommandDto convertToDto(String memberEmail, MultipartFile .location(location) .category(category) .runningMinutes(runningMinutes) - .stock(stock) - .price(price) .eventTime(eventTime) .saleStart(saleStart) .saleEnd(saleEnd) + .tickets(tickets) .build(); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockService.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockService.java index cae8cd82..885a7946 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockService.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockService.java @@ -1,5 +1,6 @@ package com.tiketeer.Tiketeer.domain.ticketing.service; +import java.util.List; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; @@ -9,6 +10,7 @@ import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.service.TicketCrudService; import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketCommandDto; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; @Service @Transactional(readOnly = true) @@ -23,23 +25,14 @@ public TicketingStockService(TicketCrudService ticketCrudService, TicketingServi } @Transactional - public void createStock(UUID ticketingId, int stock) { + public void createStock(UUID ticketingId, List tickets) { var ticketing = ticketingService.findById(ticketingId); - //TODO 마이그레이션 반영 필요 - ticketCrudService.createTickets(CreateTicketCommandDto.builder().build()); + ticketCrudService.createTickets(new CreateTicketCommandDto(ticketing.getId(), tickets)); } @Transactional - public void updateStock(UUID ticketingId, int newStock) { - var tickets = ticketCrudService.listTicketByTicketingId(ticketingId); - - var numOfTickets = tickets.size(); - if (numOfTickets > newStock) { - dropNumOfTicketsByTicketing(ticketingId, numOfTickets - newStock); - - } else if (numOfTickets < newStock) { - createStock(ticketingId, newStock - numOfTickets); - } + public void updateStock(UUID ticketingId, List tickets) { + ticketCrudService.updateTickets(ticketingId, tickets); } @Transactional diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCase.java index 73eb872e..452c7f20 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCase.java @@ -14,8 +14,8 @@ import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.exception.EventTimeNotValidException; +import com.tiketeer.Tiketeer.domain.ticketing.exception.SaleDurationNotValidException; import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingService; -import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingStockService; import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.CreateTicketingCommandDto; import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.CreateTicketingResultDto; import com.tiketeer.Tiketeer.infra.storage.FileValidator; @@ -29,7 +29,6 @@ @Service public class CreateTicketingUseCase { private final TicketingService ticketingService; - private final TicketingStockService ticketingStockService; private final MemberRepository memberRepository; private final FileStorageStrategy fileStorageStrategy; @@ -37,10 +36,9 @@ public class CreateTicketingUseCase { private StorageEnum storageEnum; @Autowired - public CreateTicketingUseCase(TicketingService ticketingService, TicketingStockService ticketingStockService, + public CreateTicketingUseCase(TicketingService ticketingService, MemberRepository memberRepository, FileStorageStrategy fileStorageStrategy) { this.ticketingService = ticketingService; - this.ticketingStockService = ticketingStockService; this.memberRepository = memberRepository; this.fileStorageStrategy = fileStorageStrategy; } @@ -56,9 +54,9 @@ public CreateTicketingResultDto createTicketing(CreateTicketingCommandDto comman throw new EventTimeNotValidException(); } - // if (!isSaleDurationValid(now, saleStart, saleEnd)) { - // throw new SaleDurationNotValidException(); - // } + if (!isSaleDurationValid(now, saleStart, saleEnd)) { + throw new SaleDurationNotValidException(); + } var member = memberRepository.findByEmail(command.getMemberEmail()) .orElseThrow(MemberNotFoundException::new); @@ -81,8 +79,6 @@ public CreateTicketingResultDto createTicketing(CreateTicketingCommandDto comman .storageEnum(storageEnum) .build()); - ticketingStockService.createStock(ticketing.getId(), command.getStock()); - return CreateTicketingResultDto.builder() .ticketingId(ticketing.getId()) .createdAt(ticketing.getCreatedAt()) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCase.java index be742c4e..13527a71 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCase.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCase.java @@ -15,7 +15,6 @@ import com.tiketeer.Tiketeer.domain.ticketing.exception.SaleDurationNotValidException; import com.tiketeer.Tiketeer.domain.ticketing.exception.UpdateTicketingAfterSaleStartException; import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingService; -import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingStockService; import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.UpdateTicketingCommandDto; import com.tiketeer.Tiketeer.infra.storage.FileValidator; import com.tiketeer.Tiketeer.infra.storage.StorageFile; @@ -28,17 +27,15 @@ @Service public class UpdateTicketingUseCase { private final TicketingService ticketingService; - private final TicketingStockService ticketingStockService; private final FileStorageStrategy fileStorageStrategy; @Value("${custom.policy.storage}") private StorageEnum storageEnum; @Autowired - public UpdateTicketingUseCase(TicketingService ticketingService, TicketingStockService ticketingStockService, + public UpdateTicketingUseCase(TicketingService ticketingService, FileStorageStrategy fileStorageStrategy) { this.ticketingService = ticketingService; - this.ticketingStockService = ticketingStockService; this.fileStorageStrategy = fileStorageStrategy; } @@ -78,7 +75,6 @@ public void updateTicketing(UpdateTicketingCommandDto command) { ticketing.setSaleEnd(saleEnd); ticketing.setCategory(command.getCategory()); ticketing.setRunningMinutes(command.getRunningMinutes()); - ticketingStockService.updateStock(ticketing.getId(), command.getStock()); } private boolean isEventTimeValid(LocalDateTime baseTime, LocalDateTime eventTime) { diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/CreateTicketingCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/CreateTicketingCommandDto.java index 3c7036d9..7e71511a 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/CreateTicketingCommandDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/CreateTicketingCommandDto.java @@ -1,9 +1,12 @@ package com.tiketeer.Tiketeer.domain.ticketing.usecase.dto; import java.time.LocalDateTime; +import java.util.List; import org.springframework.web.multipart.MultipartFile; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; + import lombok.Builder; import lombok.Getter; import lombok.ToString; @@ -17,13 +20,12 @@ public class CreateTicketingCommandDto { private final String location; private final String category; private final Integer runningMinutes; - private final Integer stock; - private final Long price; private final LocalDateTime eventTime; private final LocalDateTime saleStart; private final LocalDateTime saleEnd; private final MultipartFile thumbnail; private LocalDateTime commandCreatedAt = LocalDateTime.now(); + private final List tickets; @Builder public CreateTicketingCommandDto( @@ -33,20 +35,17 @@ public CreateTicketingCommandDto( String location, String category, Integer runningMinutes, - Integer stock, - Long price, MultipartFile thumbnail, LocalDateTime eventTime, LocalDateTime saleStart, - LocalDateTime saleEnd, LocalDateTime commandCreatedAt) { + LocalDateTime saleEnd, LocalDateTime commandCreatedAt, + List tickets) { this.memberEmail = memberEmail; this.title = title; this.description = description; this.location = location; this.category = category; this.runningMinutes = runningMinutes; - this.stock = stock; - this.price = price; this.eventTime = eventTime; this.saleStart = saleStart; this.saleEnd = saleEnd; @@ -54,5 +53,6 @@ public CreateTicketingCommandDto( if (commandCreatedAt != null) { this.commandCreatedAt = commandCreatedAt; } + this.tickets = tickets; } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/UpdateTicketingCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/UpdateTicketingCommandDto.java index 03d7303a..e16cb338 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/UpdateTicketingCommandDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/UpdateTicketingCommandDto.java @@ -1,10 +1,13 @@ package com.tiketeer.Tiketeer.domain.ticketing.usecase.dto; import java.time.LocalDateTime; +import java.util.List; import java.util.UUID; import org.springframework.web.multipart.MultipartFile; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; + import lombok.Builder; import lombok.Getter; import lombok.ToString; @@ -19,8 +22,6 @@ public class UpdateTicketingCommandDto { private final String location; private final String category; private final Integer runningMinutes; - private final Integer stock; - private final Long price; private final LocalDateTime eventTime; private final LocalDateTime saleStart; private final LocalDateTime saleEnd; @@ -36,8 +37,6 @@ public UpdateTicketingCommandDto( String location, String category, Integer runningMinutes, - Integer stock, - Long price, MultipartFile thumbnail, LocalDateTime eventTime, LocalDateTime saleStart, @@ -50,8 +49,6 @@ public UpdateTicketingCommandDto( this.location = location; this.category = category; this.runningMinutes = runningMinutes; - this.stock = stock; - this.price = price; this.eventTime = eventTime; this.thumbnail = thumbnail; this.saleStart = saleStart; diff --git a/src/test/java/com/tiketeer/Tiketeer/configuration/EmbeddedRedisConfig.java b/src/test/java/com/tiketeer/Tiketeer/configuration/EmbeddedRedisConfig.java index 3f523a52..703f87cf 100644 --- a/src/test/java/com/tiketeer/Tiketeer/configuration/EmbeddedRedisConfig.java +++ b/src/test/java/com/tiketeer/Tiketeer/configuration/EmbeddedRedisConfig.java @@ -7,8 +7,9 @@ import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @@ -22,7 +23,8 @@ @Slf4j @DisplayName("Embedded Redis 설정") -@TestConfiguration +@Configuration +@Profile("test") @EnableRedisRepositories public class EmbeddedRedisConfig { diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java index a9daace0..1724c454 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseConcurrencyTest.java @@ -15,6 +15,7 @@ import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseCommandDto; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingStockService; @@ -62,7 +63,7 @@ public int makeConcurrency(int threadNums, List buyers, Ticketing ticket return success.get(); } - public Ticketing createTicketing(Member member, int stock) { + public Ticketing createTicketing(Member member, List tickets) { var now = LocalDateTime.now(); var ticketing = ticketingRepository.save(Ticketing.builder() .member(member) @@ -74,7 +75,7 @@ public Ticketing createTicketing(Member member, int stock) { .saleEnd(now.plusMonths(6)) .eventTime(now.plusYears(1)) .build()); - ticketingStockService.createStock(ticketing.getId(), stock); + ticketingStockService.createStock(ticketing.getId(), tickets); return ticketing; } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java index b48f1cb7..b5a1c3d6 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseDistributedLockConcurrencyTest.java @@ -4,6 +4,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.AfterEach; @@ -22,7 +23,7 @@ import com.tiketeer.Tiketeer.domain.purchase.usecase.decorator.CreatePurchaseUseCaseDLock; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingStockService; import com.tiketeer.Tiketeer.testhelper.RedisTestHelper; @@ -66,9 +67,13 @@ void cleanTable() { @DisplayName("10개의 티켓 생성 > 20명의 구매자가 경쟁 > 10명 구매 성공, 10명 구매 실패") void createPurchaseWithConcurrency() throws InterruptedException { //given - var ticketStock = 10; + var ticketsMeta = new ArrayList<>(Arrays.asList( + new CreateTicketMetadata(10, 10, "Gold", "", 3000), + new CreateTicketMetadata(10, 10, "Silver", "", 2000), + new CreateTicketMetadata(10, 10, "Bronze", "", 1000)) + ); var seller = testHelper.createMember("seller@etest.com", RoleEnum.SELLER); - var ticketing = createTicketing(seller, ticketStock); + var ticketing = createPurchaseConcurrencyTest.createTicketing(seller, ticketsMeta); int threadNums = 20; var buyers = createBuyers(threadNums); @@ -78,38 +83,19 @@ void createPurchaseWithConcurrency() throws InterruptedException { //then transaction.invoke(() -> { var tickets = ticketRepository.findAll(); - assertThat(tickets.size()).isEqualTo(0); - - //assert all ticket owners are unique - var purchasedTickets = ticketRepository.findAll(); - assertThat(purchasedTickets.size()).isEqualTo(ticketStock); + assertThat(tickets.size()).isEqualTo(3); //assert one purchase per member - var allMembers = memberRepository.findAll(); - var ticketingSuccessMembers = allMembers.stream() - .filter(member -> member.getPurchases().size() == 1) - .toList(); - assertThat(ticketingSuccessMembers.size()).isEqualTo(ticketStock); + // TODO: migration 반영 필요 + // var allMembers = memberRepository.findAll(); + // var ticketingSuccessMembers = allMembers.stream() + // .filter(member -> member.getPurchases().size() == 1) + // .toList(); + // assertThat(ticketingSuccessMembers.size()).isEqualTo(ticketStock); return null; }); } - private Ticketing createTicketing(Member member, int stock) { - var now = LocalDateTime.now(); - var ticketing = ticketingRepository.save(Ticketing.builder() - .member(member) - .title("제목") - .location("서울") - .category("바자회") - .runningMinutes(100) - .saleStart(now.minusMonths(6)) - .saleEnd(now.plusMonths(6)) - .eventTime(now.plusYears(1)) - .build()); - ticketingStockService.createStock(ticketing.getId(), stock); - return ticketing; - } - private List createBuyers(int buyerNum) { List buyers = new ArrayList<>(); for (int i = 0; i < buyerNum; i++) { diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java index 5b98c1ed..2eed28be 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseOptimisticLockConcurrencyTest.java @@ -4,6 +4,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.AfterEach; @@ -21,6 +22,7 @@ import com.tiketeer.Tiketeer.domain.purchase.usecase.decorator.CreatePurchaseUseCaseOLock; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingStockService; @@ -60,9 +62,13 @@ void cleanTable() { @DisplayName("10개의 티켓 생성 > 20명의 구매자가 경쟁 > 10명 구매 성공, 10명 구매 실패") void createPurchaseWithConcurrency() throws InterruptedException { //given - var ticketStock = 10; + var ticketsMeta = new ArrayList<>(Arrays.asList( + new CreateTicketMetadata(10, 10, "Gold", "", 3000), + new CreateTicketMetadata(10, 10, "Silver", "", 2000), + new CreateTicketMetadata(10, 10, "Bronze", "", 1000)) + ); var seller = testHelper.createMember("seller@etest.com", RoleEnum.SELLER); - var ticketing = createTicketing(seller, ticketStock); + var ticketing = createPurchaseConcurrencyTest.createTicketing(seller, ticketsMeta); int threadNums = 30; var buyers = createBuyers(threadNums); @@ -72,7 +78,9 @@ void createPurchaseWithConcurrency() throws InterruptedException { //then transaction.invoke(() -> { var tickets = ticketRepository.findAll(); - assertThat(ticketStock - tickets.size()).isEqualTo(success); + + // TODO: migration 반영 필요 + // assertThat(ticketStock - tickets.size()).isEqualTo(success); var allMembers = memberRepository.findAll(); @@ -90,22 +98,6 @@ void createPurchaseWithConcurrency() throws InterruptedException { }); } - private Ticketing createTicketing(Member member, int stock) { - var now = LocalDateTime.now(); - var ticketing = ticketingRepository.save(Ticketing.builder() - .member(member) - .title("제목") - .location("서울") - .category("바자회") - .runningMinutes(100) - .saleStart(now.minusMonths(6)) - .saleEnd(now.plusMonths(6)) - .eventTime(now.plusYears(1)) - .build()); - ticketingStockService.createStock(ticketing.getId(), stock); - return ticketing; - } - private List createBuyers(int buyerNum) { List buyers = new ArrayList<>(); for (int i = 0; i < buyerNum; i++) { diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java index acb30874..0ea8aaf2 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCasePessimisticLockConcurrencyTest.java @@ -4,6 +4,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.AfterEach; @@ -21,6 +22,7 @@ import com.tiketeer.Tiketeer.domain.purchase.usecase.decorator.CreatePurchaseUseCasePLock; import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; import com.tiketeer.Tiketeer.domain.ticketing.service.TicketingStockService; @@ -61,8 +63,13 @@ void cleanTable() { void createPurchaseWithConcurrency() throws InterruptedException { //given var ticketStock = 10; + var ticketsMeta = new ArrayList<>(Arrays.asList( + new CreateTicketMetadata(10, 10, "Gold", "", 3000), + new CreateTicketMetadata(10, 10, "Silver", "", 2000), + new CreateTicketMetadata(10, 10, "Bronze", "", 1000)) + ); var seller = testHelper.createMember("seller@etest.com", RoleEnum.SELLER); - var ticketing = createTicketing(seller, ticketStock); + var ticketing = createPurchaseConcurrencyTest.createTicketing(seller, ticketsMeta); int threadNums = 20; var buyers = createBuyers(threadNums); @@ -90,21 +97,6 @@ void createPurchaseWithConcurrency() throws InterruptedException { }); } - private Ticketing createTicketing(Member member, int stock) { - var now = LocalDateTime.now(); - var ticketing = ticketingRepository.save(Ticketing.builder() - .member(member) - .title("제목") - .location("서울") - .category("바자회") - .runningMinutes(100) - .saleStart(now.minusMonths(6)) - .saleEnd(now.plusMonths(6)) - .eventTime(now.plusYears(1)) - .build()); - ticketingStockService.createStock(ticketing.getId(), stock); - return ticketing; - } private List createBuyers(int buyerNum) { List buyers = new ArrayList<>(); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java index 5ada3655..e3632aa0 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java @@ -40,6 +40,7 @@ import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.controller.dto.GetAllTicketingsResponseDto; import com.tiketeer.Tiketeer.domain.ticketing.controller.dto.GetTicketingResponseDto; @@ -80,13 +81,13 @@ public class TicketingControllerTest { @BeforeEach void setup() { - // testHelper.initDB(); + testHelper.initDB(); System.setProperty("custom.policy.local-storage-path", tempDir.toString()); } @AfterEach void tearDown() { - // testHelper.cleanDB(); + testHelper.cleanDB(); System.clearProperty("custom.policy.local-storage-path"); } @@ -180,11 +181,13 @@ void postTicketingSuccess() throws Exception { .location("서울 강남역 8번 출구") .category("음악회") .runningMinutes(100) - .price(10000L) - .stock(20) .eventTime(now.plusYears(3)) .saleStart(now.plusYears(1)) .saleEnd(now.plusYears(2)) + .tickets(Arrays.asList( + new CreateTicketMetadata(10, 10, "Gold", "", 2000), + new CreateTicketMetadata(10, 10, "Silver", "", 1000) + )) .build(); var content = objectMapper.writeValueAsString(req); @@ -227,21 +230,17 @@ void patchTicketingSuccess() throws Exception { var title = "음악회"; var runningMinutes = 100; - var stock = 20; - var createTicketingCmd = createCreateTicketingCommand(email, title, runningMinutes, stock); + var createTicketingCmd = createCreateTicketingCommand(email, title, runningMinutes); var ticketingId = createTicketingUseCase.createTicketing(createTicketingCmd).getTicketingId(); var updatedTitle = "음악회1"; var updatedRunningMinutes = 120; - var updatedStock = 50; var req = PatchTicketingRequestDto.builder() .title(updatedTitle) .description(createTicketingCmd.getDescription()) .location(createTicketingCmd.getLocation()) .category(createTicketingCmd.getCategory()) .runningMinutes(updatedRunningMinutes) - .price(createTicketingCmd.getPrice()) - .stock(updatedStock) .eventTime(createTicketingCmd.getEventTime()) .saleStart(createTicketingCmd.getSaleStart()) .saleEnd(createTicketingCmd.getSaleEnd()) @@ -272,15 +271,12 @@ void patchTicketingSuccess() throws Exception { ).andExpect(status().is2xxSuccessful()); // then - var ticketingInfoList = ticketingRepository.findTicketingWithTicketStock(email); - Assertions.assertThat(ticketingInfoList.size()).isEqualTo(1); + var ticketingInfoOpt = ticketingRepository.findById(ticketingId); - var ticketingInfo = ticketingInfoList.getFirst(); - Assertions.assertThat(ticketingInfo.getTicketingId()).isEqualTo(ticketingId); - Assertions.assertThat(ticketingInfo.getPrice()).isEqualTo(createTicketingCmd.getPrice()); + var ticketingInfo = ticketingInfoOpt.get(); + Assertions.assertThat(ticketingInfo.getId()).isEqualTo(ticketingId); Assertions.assertThat(ticketingInfo.getCategory()).isEqualTo(createTicketingCmd.getCategory()); Assertions.assertThat(ticketingInfo.getTitle()).isEqualTo(updatedTitle); - Assertions.assertThat(ticketingInfo.getRemainStock()).isEqualTo(updatedStock); Assertions.assertThat(ticketingInfo.getRunningMinutes()).isEqualTo(updatedRunningMinutes); } @@ -294,8 +290,7 @@ void deleteTicketingSuccess() throws Exception { var title = "음악회"; var runningMinutes = 100; - var stock = 20; - var createTicketingCmd = createCreateTicketingCommand(email, title, runningMinutes, stock); + var createTicketingCmd = createCreateTicketingCommand(email, title, runningMinutes); var ticketingId = createTicketingUseCase.createTicketing(createTicketingCmd).getTicketingId(); // when @@ -338,20 +333,22 @@ private List createTickets(Ticketing ticketing, int stock) { .toList()); } - private CreateTicketingCommandDto createCreateTicketingCommand(String email, String title, int runningMinutes, - int stock) { + private CreateTicketingCommandDto createCreateTicketingCommand(String email, String title, int runningMinutes) { var now = LocalDateTime.now(); return CreateTicketingCommandDto.builder() .memberEmail(email) .title(title) - .price(1000L) .category("카테고리") .location("서울") - .stock(stock) .runningMinutes(runningMinutes) .saleStart(now.plusYears(1)) .saleEnd(now.plusYears(2)) - .eventTime(now.plusYears(3)).build(); + .eventTime(now.plusYears(3)) + .tickets(Arrays.asList( + new CreateTicketMetadata(10, 10, "Gold", "", 2000), + new CreateTicketMetadata(10, 10, "Silver", "", 1000) + )) + .build(); } private void wrapWithTimeConsole(Runnable runnable) { diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockServiceTest.java index 22640623..7a38e8bf 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockServiceTest.java @@ -1,6 +1,8 @@ package com.tiketeer.Tiketeer.domain.ticketing.service; import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; import java.util.UUID; import org.assertj.core.api.Assertions; @@ -15,6 +17,7 @@ import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; @@ -52,7 +55,10 @@ void createStockFailBecauseNotExistTicketing() { Assertions.assertThatThrownBy(() -> { // when - ticketingStockService.createStock(invalidTicketingId, 100); + ticketingStockService.createStock( + invalidTicketingId, + List.of(new CreateTicketMetadata(10, 10, "Gold", "", 1000) + )); // then }).isInstanceOf(TicketingNotFoundException.class); } @@ -71,11 +77,14 @@ void createStockSuccess() { Assertions.assertThat(ticketRepository.findAllByTicketing(ticketing)).isEmpty(); // when - ticketingStockService.createStock(ticketing.getId(), addStock); + ticketingStockService.createStock( + ticketing.getId(), + List.of(new CreateTicketMetadata(addStock, addStock, "Gold", "", 1000)) + ); // then var tickets = ticketRepository.findAllByTicketing(ticketing); - Assertions.assertThat(tickets.size()).isEqualTo(addStock); + Assertions.assertThat(tickets.getFirst().getStock()).isEqualTo(addStock); } @Test @@ -86,7 +95,8 @@ void updateStockFailBecauseNotExistTicketing() { Assertions.assertThatThrownBy(() -> { // when - ticketingStockService.updateStock(invalidTicketingId, 10); + ticketingStockService.updateStock(invalidTicketingId, + List.of(new CreateTicketMetadata(1, 1, "Gold", "", 1000))); // then }).isInstanceOf(TicketingNotFoundException.class); } @@ -101,21 +111,21 @@ void updateStockSuccessForAddRequest() { var ticketing = createTicketing(member); var initStock = 10; - ticketingStockService.createStock(ticketing.getId(), initStock); + ticketingStockService.createStock(ticketing.getId(), List.of(new CreateTicketMetadata(initStock, initStock, "Gold", "", 1000))); - Assertions.assertThat(ticketRepository.findAllByTicketing(ticketing).size()).isEqualTo(initStock); + Assertions.assertThat(ticketRepository.findAllByTicketing(ticketing).getFirst().getStock()).isEqualTo(initStock); // when var updateStock = 20; - ticketingStockService.updateStock(ticketing.getId(), updateStock); + ticketingStockService.updateStock(ticketing.getId(), List.of(new CreateTicketMetadata(updateStock, updateStock, "Gold", "", 1000))); // then var tickets = ticketRepository.findAllByTicketing(ticketing); - Assertions.assertThat(tickets.size()).isEqualTo(updateStock); + Assertions.assertThat(tickets.getFirst().getStock()).isEqualTo(updateStock); } @Test - @DisplayName("기존 재고의 수(30) > 재고 수정 요청(10) > 재고 업데이트 성공 (30 -> 10)") + @DisplayName("기존 티켓의 수(1) > 티켓 추가 업데이트 > 티켓 업데이트 성공 (1 -> 2)") @Transactional void updateStockSuccessForRemoveRequest() { // given @@ -124,17 +134,20 @@ void updateStockSuccessForRemoveRequest() { var ticketing = createTicketing(member); var initStock = 30; - ticketingStockService.createStock(ticketing.getId(), initStock); + ticketingStockService.createStock(ticketing.getId(), List.of(new CreateTicketMetadata(initStock, initStock, "Gold", "", 1000))); - Assertions.assertThat(ticketRepository.findAllByTicketing(ticketing).size()).isEqualTo(initStock); + Assertions.assertThat(ticketRepository.findAllByTicketing(ticketing).size()).isEqualTo(1); // when var updateStock = 10; - ticketingStockService.updateStock(ticketing.getId(), updateStock); + var ticketsMeta = Arrays.asList( + new CreateTicketMetadata(initStock, initStock, "Gold", "", 2000), + new CreateTicketMetadata(initStock, initStock, "Silver", "", 1000)); + ticketingStockService.updateStock(ticketing.getId(), ticketsMeta); // then var tickets = ticketRepository.findAllByTicketing(ticketing); - Assertions.assertThat(tickets.size()).isEqualTo(updateStock); + Assertions.assertThat(tickets.size()).isEqualTo(2); } @Test @@ -160,9 +173,13 @@ void dropAllStockSuccess() { var ticketing = createTicketing(member); var initStock = 30; - ticketingStockService.createStock(ticketing.getId(), initStock); + var ticketsMeta = Arrays.asList( + new CreateTicketMetadata(initStock, initStock, "Gold", "", 1000), + new CreateTicketMetadata(initStock, initStock, "Silver", "", 1000) + ); + ticketingStockService.createStock(ticketing.getId(), ticketsMeta); - Assertions.assertThat(ticketRepository.findAllByTicketing(ticketing).size()).isEqualTo(initStock); + Assertions.assertThat(ticketRepository.findAllByTicketing(ticketing).size()).isEqualTo(2); // when ticketingStockService.dropAllStock(ticketing.getId()); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCaseTest.java index 14f90760..f964e755 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCaseTest.java @@ -4,6 +4,9 @@ import java.nio.file.Path; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -21,6 +24,7 @@ import com.tiketeer.Tiketeer.constant.StorageEnum; import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.exception.EventTimeNotValidException; import com.tiketeer.Tiketeer.domain.ticketing.exception.SaleDurationNotValidException; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; @@ -66,7 +70,10 @@ void createTicketingFailBecauseInvalidEventTime() { var saleStart = now.plusYears(1); var saleEnd = now.plusYears(2); var eventTime = now.minusYears(20); - var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null); + + var ticketingTickets = createTicketingTickets(); + + var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null, ticketingTickets); assertThatThrownBy(() -> { // when @@ -86,7 +93,8 @@ void createTicketingFailBecauseInvalidSaleDuration() { var saleStart = now.plusYears(2); var saleEnd = now.plusYears(1); var eventTime = now.plusYears(3); - var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null); + var ticketingTickets = createTicketingTickets(); + var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null, ticketingTickets); assertThatThrownBy(() -> { // when @@ -106,7 +114,8 @@ void createTicketingFailBecauseEventTimeDuringSaleDuration() { var saleStart = now.plusYears(1); var saleEnd = now.plusYears(3); var eventTime = now.plusYears(2); - var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null); + var ticketingTickets = createTicketingTickets(); + var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null, ticketingTickets); assertThatThrownBy(() -> { // when @@ -125,7 +134,8 @@ void createTicketingFailBecauseInvalidEmail() { var saleStart = now.plusYears(1); var saleEnd = now.plusYears(2); var eventTime = now.plusYears(3); - var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null); + var ticketingTickets = createTicketingTickets(); + var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null, ticketingTickets); assertThatThrownBy(() -> { // when @@ -145,11 +155,12 @@ void createTicketingSuccess() { var saleStart = now.plusYears(1); var saleEnd = now.plusYears(2); var eventTime = now.plusYears(3); + var ticketingTickets = createTicketingTickets(); MockMultipartFile image = new MockMultipartFile( "thumbnail", "image.png", "image/png", "Image Content".getBytes() ); - var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, image); + var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, image, ticketingTickets); // when var result = createTicketingUseCase.createTicketing(command); @@ -168,26 +179,37 @@ void createTicketingSuccess() { assertThat(ticketing.getStorageEnum()).isEqualTo(StorageEnum.LOCAL); var tickets = ticketRepository.findAllByTicketing(ticketing); - assertThat(tickets.size()).isEqualTo(command.getStock()); + // assertThat(tickets.size()).isEqualTo(command.getStock()); } private CreateTicketingCommandDto createTicketingCommand(String email, LocalDateTime eventTime, - LocalDateTime saleStart, LocalDateTime saleEnd, MultipartFile thumbnail) { + LocalDateTime saleStart, LocalDateTime saleEnd, MultipartFile thumbnail, + List tickets) { return CreateTicketingCommandDto.builder() .memberEmail(email) .title("음악회") .location("서울 강남역 8번 출구") .category("음악회") .runningMinutes(100) - .price(10000L) - .stock(20) .eventTime(eventTime) .saleStart(saleStart) .saleEnd(saleEnd) .thumbnail(thumbnail) + .tickets(tickets) .build(); } + private List createTicketingTickets() { + List titles = new ArrayList<>(Arrays.asList("Gold", "Silver", "Bronze")); + List prices = new ArrayList<>(Arrays.asList(3000L, 2000L, 1000L)); + List stocks = new ArrayList<>(Arrays.asList(10, 10, 10)); + + return Arrays.stream(new int[3]) + .mapToObj(i -> + new CreateTicketMetadata(stocks.get(i), stocks.get(i), titles.get(i), "", prices.get(i))) + .toList(); + } + @Test @DisplayName("이미지가 아닌 썸네일 파일 > 티켓팅 생성 요청 > 실패") void createTicketingFailInvalidFileType() { @@ -199,12 +221,13 @@ void createTicketingFailInvalidFileType() { var saleStart = now.plusYears(1); var saleEnd = now.plusYears(2); var eventTime = now.plusYears(3); + var ticketingTickets = createTicketingTickets(); MockMultipartFile file = new MockMultipartFile( "thumbnail", "image.txt", "text/plain", "Image Content".getBytes() ); - var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, file); + var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, file, ticketingTickets); assertThatThrownBy(() -> { // when diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/DeleteTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/DeleteTicketingUseCaseTest.java index 5daa6b14..788236ad 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/DeleteTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/DeleteTicketingUseCaseTest.java @@ -1,6 +1,7 @@ package com.tiketeer.Tiketeer.domain.ticketing.usecase; import java.time.LocalDateTime; +import java.util.Arrays; import java.util.UUID; import org.assertj.core.api.Assertions; @@ -14,6 +15,7 @@ import org.springframework.transaction.annotation.Transactional; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.exception.DeleteTicketingAfterSaleStartException; import com.tiketeer.Tiketeer.domain.ticketing.exception.ModifyForNotOwnedTicketingException; import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; @@ -131,7 +133,7 @@ void deleteTicketingSuccess() { var ticketing = ticketingOpt.get(); Assertions.assertThat(ticketRepository.findAllByTicketing(ticketing).size()) - .isEqualTo(createTicketingCommand.getStock()); + .isEqualTo(createTicketingCommand.getTickets().size()); var deleteTicketingCommand = DeleteTicketingCommandDto.builder() .ticketingId(ticketingId) @@ -159,11 +161,13 @@ private CreateTicketingCommandDto createTicketingCommand(String email, LocalDate .location("서울 강남역 8번 출구") .category("음악회") .runningMinutes(100) - .price(10000L) - .stock(20) .eventTime(eventTime) .saleStart(saleStart) .saleEnd(saleEnd) + .tickets(Arrays.asList( + new CreateTicketMetadata(10, 10, "Gold", "", 2000), + new CreateTicketMetadata(10, 10, "Silver", "", 1000) + )) .build(); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java index 1c77223c..ffa20fd2 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java @@ -2,6 +2,7 @@ import java.nio.file.Path; import java.time.LocalDateTime; +import java.util.Arrays; import java.util.UUID; import org.assertj.core.api.Assertions; @@ -17,7 +18,9 @@ import org.springframework.context.annotation.Import; import org.springframework.transaction.annotation.Transactional; +import com.tiketeer.Tiketeer.configuration.EmbeddedRedisConfig; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.exception.EventTimeNotValidException; import com.tiketeer.Tiketeer.domain.ticketing.exception.ModifyForNotOwnedTicketingException; import com.tiketeer.Tiketeer.domain.ticketing.exception.SaleDurationNotValidException; @@ -104,11 +107,9 @@ void updateTicketingFailBecauseNotOwnedTicketing() { .ticketingId(ticketingId) .email(memberEmailNotOwnedTicketing) .title(createCmd.getTitle()) - .price(createCmd.getPrice()) .description(createCmd.getDescription()) .category(createCmd.getCategory()) .runningMinutes(createCmd.getRunningMinutes()) - .stock(createCmd.getStock()) .saleStart(createCmd.getSaleStart()) .saleEnd(createCmd.getSaleEnd()) .eventTime(createCmd.getEventTime()) @@ -141,11 +142,9 @@ void updateTicketingFailBecauseSaleDurationHasBeenStarted() { .ticketingId(ticketingId) .email(createCmd.getMemberEmail()) .title(createCmd.getTitle()) - .price(createCmd.getPrice()) .description(createCmd.getDescription()) .category(createCmd.getCategory()) .runningMinutes(createCmd.getRunningMinutes()) - .stock(createCmd.getStock()) .saleStart(createCmd.getSaleStart()) .saleEnd(createCmd.getSaleEnd()) .eventTime(createCmd.getEventTime()) @@ -178,11 +177,9 @@ void updateTicketingFailBecauseInvalidEventTime() { .ticketingId(ticketingId) .email(createCmd.getMemberEmail()) .title(createCmd.getTitle()) - .price(createCmd.getPrice()) .description(createCmd.getDescription()) .category(createCmd.getCategory()) .runningMinutes(createCmd.getRunningMinutes()) - .stock(createCmd.getStock()) .saleStart(createCmd.getSaleStart()) .saleEnd(createCmd.getSaleEnd()) .eventTime(now.minusDays(1)) @@ -214,11 +211,9 @@ void updateTicketingFailBecauseSaleDurationNotValid() { .ticketingId(ticketingId) .email(createCmd.getMemberEmail()) .title(createCmd.getTitle()) - .price(createCmd.getPrice()) .description(createCmd.getDescription()) .category(createCmd.getCategory()) .runningMinutes(createCmd.getRunningMinutes()) - .stock(createCmd.getStock()) .saleStart(saleEnd) .saleEnd(saleStart) .eventTime(eventTime) @@ -250,11 +245,9 @@ void updateTicketingFailBecauseEventTimeBeforeSaleEnd() { .ticketingId(ticketingId) .email(createCmd.getMemberEmail()) .title(createCmd.getTitle()) - .price(createCmd.getPrice()) .description(createCmd.getDescription()) .category(createCmd.getCategory()) .runningMinutes(createCmd.getRunningMinutes()) - .stock(createCmd.getStock()) .saleStart(saleStart) .saleEnd(saleEnd) .eventTime(saleEnd.minusDays(1)) @@ -284,11 +277,9 @@ void updateTicketingSuccess() { var newTitle = "New Title!"; var newDescription = "New!!!"; - var newPrice = createCmd.getPrice() * 2; var newCategory = createCmd.getCategory() + "!"; var newLocation = createCmd.getLocation() + "!"; var newRunningMinutes = createCmd.getRunningMinutes() * 2; - var newStock = createCmd.getStock() + 10; var newSaleStart = createCmd.getSaleStart().plusMonths(1); var newSaleEnd = createCmd.getSaleEnd().plusMonths(1); var newEventTime = createCmd.getEventTime().plusMonths(1); @@ -297,12 +288,10 @@ void updateTicketingSuccess() { .ticketingId(ticketingId) .email(createCmd.getMemberEmail()) .title(newTitle) - .price(newPrice) .location(newLocation) .description(newDescription) .category(newCategory) .runningMinutes(newRunningMinutes) - .stock(newStock) .saleStart(newSaleStart) .saleEnd(newSaleEnd) .eventTime(newEventTime) @@ -324,9 +313,6 @@ void updateTicketingSuccess() { Assertions.assertThat(updatedTicketing.getEventTime()).isEqualToIgnoringNanos(newEventTime); Assertions.assertThat(updatedTicketing.getSaleStart()).isEqualToIgnoringNanos(newSaleStart); Assertions.assertThat(updatedTicketing.getSaleEnd()).isEqualToIgnoringNanos(newSaleEnd); - - var tickets = ticketRepository.findAllByTicketing(updatedTicketing); - Assertions.assertThat(tickets.size()).isEqualTo(newStock); } private CreateTicketingCommandDto createTicketingCommand(String email, LocalDateTime eventTime, @@ -337,11 +323,13 @@ private CreateTicketingCommandDto createTicketingCommand(String email, LocalDate .location("서울 강남역 8번 출구") .category("음악회") .runningMinutes(100) - .price(10000L) - .stock(20) .eventTime(eventTime) .saleStart(saleStart) .saleEnd(saleEnd) + .tickets(Arrays.asList( + new CreateTicketMetadata(10, 10, "Gold", "", 2000), + new CreateTicketMetadata(10, 10, "Silver", "", 1000) + )) .build(); } } diff --git a/src/test/java/com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java b/src/test/java/com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java index 7b278266..1be97aa9 100644 --- a/src/test/java/com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java @@ -3,6 +3,8 @@ import static org.assertj.core.api.Assertions.*; import java.time.LocalDateTime; +import java.util.Arrays; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -14,6 +16,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; import com.tiketeer.Tiketeer.domain.ticketing.usecase.CreateTicketingUseCase; import com.tiketeer.Tiketeer.domain.ticketing.usecase.DeleteTicketingUseCase; @@ -91,10 +94,8 @@ void cacheUpdateTest() throws JsonProcessingException { // when UpdateTicketingCommandDto updateTicketingCommandDto = UpdateTicketingCommandDto.builder() .email(email) - .price(1000L) .category("") .runningMinutes(100) - .stock(1) .saleStart(changedSaleStart) .saleEnd(changedSaleEnd) .location("") @@ -144,9 +145,7 @@ void cacheDeleteTest() throws JsonProcessingException { private CreateTicketingResultDto createTicketing(String email, LocalDateTime saleStart, LocalDateTime saleEnd) { CreateTicketingCommandDto createTicketingCommandDto = CreateTicketingCommandDto.builder() - .price(1000L) .runningMinutes(100) - .stock(1) .memberEmail(email) .saleEnd(saleEnd) .saleStart(saleStart) @@ -155,6 +154,10 @@ private CreateTicketingResultDto createTicketing(String email, LocalDateTime sal .location("") .category("") .description("") + .tickets(Arrays.asList( + new CreateTicketMetadata(10, 10, "Gold", "", 2000), + new CreateTicketMetadata(10, 10, "Silver", "", 1000) + )) .build(); return createTicketingUseCase.createTicketing(createTicketingCommandDto); } diff --git a/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelperTest.java b/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelperTest.java index 95b4cf26..1a1904e3 100644 --- a/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelperTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/testhelper/TestHelperTest.java @@ -123,7 +123,7 @@ void cleanDB() { var mockPurchase = purchaseRepository.save(Purchase.builder().member(mockMember).build()); - ticketRepository.save(Ticket.builder().ticketing(mockTicketing).build()); + ticketRepository.save(Ticket.builder().ticketing(mockTicketing).title("test").stock(1).remainingStock(1).description("").build()); var repoForTestList = List.of( ticketingRepository, From db66ccb006096d93b219083d2c1b26686fb5e6b4 Mon Sep 17 00:00:00 2001 From: punkryn Date: Fri, 14 Jun 2024 10:52:19 +0900 Subject: [PATCH 26/38] =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ticketing/controller/dto/PostTicketingRequestDto.java | 8 +------- .../ticketing/usecase/dto/CreateTicketingCommandDto.java | 5 +---- .../ticketing/controller/TicketingControllerTest.java | 8 -------- .../ticketing/usecase/CreateTicketingUseCaseTest.java | 1 - .../ticketing/usecase/DeleteTicketingUseCaseTest.java | 6 ------ .../ticketing/usecase/UpdateTicketingUseCaseTest.java | 4 ---- .../com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java | 4 ---- 7 files changed, 2 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PostTicketingRequestDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PostTicketingRequestDto.java index 57e325bd..d97fcaa8 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PostTicketingRequestDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PostTicketingRequestDto.java @@ -46,9 +46,6 @@ public class PostTicketingRequestDto { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") private final LocalDateTime saleEnd; - @NotNull - private final List tickets; - @Builder public PostTicketingRequestDto( @NotBlank String title, @@ -58,8 +55,7 @@ public PostTicketingRequestDto( @NotNull Integer runningMinutes, @NotNull LocalDateTime eventTime, @NotNull LocalDateTime saleStart, - @NotNull LocalDateTime saleEnd, - @NotNull List tickets) { + @NotNull LocalDateTime saleEnd) { this.title = title; this.description = description; this.location = location; @@ -68,7 +64,6 @@ public PostTicketingRequestDto( this.eventTime = eventTime; this.saleStart = saleStart; this.saleEnd = saleEnd; - this.tickets = tickets; } public CreateTicketingCommandDto convertToDto(String memberEmail, MultipartFile thumbnail) { @@ -83,7 +78,6 @@ public CreateTicketingCommandDto convertToDto(String memberEmail, MultipartFile .eventTime(eventTime) .saleStart(saleStart) .saleEnd(saleEnd) - .tickets(tickets) .build(); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/CreateTicketingCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/CreateTicketingCommandDto.java index 7e71511a..9758a7cc 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/CreateTicketingCommandDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/CreateTicketingCommandDto.java @@ -25,7 +25,6 @@ public class CreateTicketingCommandDto { private final LocalDateTime saleEnd; private final MultipartFile thumbnail; private LocalDateTime commandCreatedAt = LocalDateTime.now(); - private final List tickets; @Builder public CreateTicketingCommandDto( @@ -38,8 +37,7 @@ public CreateTicketingCommandDto( MultipartFile thumbnail, LocalDateTime eventTime, LocalDateTime saleStart, - LocalDateTime saleEnd, LocalDateTime commandCreatedAt, - List tickets) { + LocalDateTime saleEnd, LocalDateTime commandCreatedAt) { this.memberEmail = memberEmail; this.title = title; this.description = description; @@ -53,6 +51,5 @@ public CreateTicketingCommandDto( if (commandCreatedAt != null) { this.commandCreatedAt = commandCreatedAt; } - this.tickets = tickets; } } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java index e3632aa0..9d83041a 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java @@ -184,10 +184,6 @@ void postTicketingSuccess() throws Exception { .eventTime(now.plusYears(3)) .saleStart(now.plusYears(1)) .saleEnd(now.plusYears(2)) - .tickets(Arrays.asList( - new CreateTicketMetadata(10, 10, "Gold", "", 2000), - new CreateTicketMetadata(10, 10, "Silver", "", 1000) - )) .build(); var content = objectMapper.writeValueAsString(req); @@ -344,10 +340,6 @@ private CreateTicketingCommandDto createCreateTicketingCommand(String email, Str .saleStart(now.plusYears(1)) .saleEnd(now.plusYears(2)) .eventTime(now.plusYears(3)) - .tickets(Arrays.asList( - new CreateTicketMetadata(10, 10, "Gold", "", 2000), - new CreateTicketMetadata(10, 10, "Silver", "", 1000) - )) .build(); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCaseTest.java index f964e755..beaba2bd 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCaseTest.java @@ -195,7 +195,6 @@ private CreateTicketingCommandDto createTicketingCommand(String email, LocalDate .saleStart(saleStart) .saleEnd(saleEnd) .thumbnail(thumbnail) - .tickets(tickets) .build(); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/DeleteTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/DeleteTicketingUseCaseTest.java index 788236ad..23e8607e 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/DeleteTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/DeleteTicketingUseCaseTest.java @@ -132,8 +132,6 @@ void deleteTicketingSuccess() { Assertions.assertThat(ticketingOpt.isPresent()).isTrue(); var ticketing = ticketingOpt.get(); - Assertions.assertThat(ticketRepository.findAllByTicketing(ticketing).size()) - .isEqualTo(createTicketingCommand.getTickets().size()); var deleteTicketingCommand = DeleteTicketingCommandDto.builder() .ticketingId(ticketingId) @@ -164,10 +162,6 @@ private CreateTicketingCommandDto createTicketingCommand(String email, LocalDate .eventTime(eventTime) .saleStart(saleStart) .saleEnd(saleEnd) - .tickets(Arrays.asList( - new CreateTicketMetadata(10, 10, "Gold", "", 2000), - new CreateTicketMetadata(10, 10, "Silver", "", 1000) - )) .build(); } diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java index ffa20fd2..d635698b 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java @@ -326,10 +326,6 @@ private CreateTicketingCommandDto createTicketingCommand(String email, LocalDate .eventTime(eventTime) .saleStart(saleStart) .saleEnd(saleEnd) - .tickets(Arrays.asList( - new CreateTicketMetadata(10, 10, "Gold", "", 2000), - new CreateTicketMetadata(10, 10, "Silver", "", 1000) - )) .build(); } } diff --git a/src/test/java/com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java b/src/test/java/com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java index 1be97aa9..e95d71c2 100644 --- a/src/test/java/com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java @@ -154,10 +154,6 @@ private CreateTicketingResultDto createTicketing(String email, LocalDateTime sal .location("") .category("") .description("") - .tickets(Arrays.asList( - new CreateTicketMetadata(10, 10, "Gold", "", 2000), - new CreateTicketMetadata(10, 10, "Silver", "", 1000) - )) .build(); return createTicketingUseCase.createTicketing(createTicketingCommandDto); } From 2f3c423bb2b5e89a181a95f5cc957c09c6474157 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Sat, 15 Jun 2024 10:49:44 +0900 Subject: [PATCH 27/38] =?UTF-8?q?TicketNotFoundException=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/CreatePurchaseUseCaseCore.java | 6 ++++ .../usecase/CreatePurchaseUseCaseTest.java | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java index 0cf2658b..2ecdaa14 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseCore.java @@ -19,6 +19,7 @@ import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseCommandDto; import com.tiketeer.Tiketeer.domain.purchase.usecase.dto.CreatePurchaseResultDto; import com.tiketeer.Tiketeer.domain.ticket.Ticket; +import com.tiketeer.Tiketeer.domain.ticket.exception.TicketNotFoundException; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticket.service.TicketStockService; @@ -76,6 +77,11 @@ private CreatePurchaseResultDto execPurchaseLogic(CreatePurchaseCommandDto comma var ticketIds = command.getTickets().stream().map(CreatePurchaseCommandDto.Ticket::getId).toList(); var tickets = listTicketStrategy.findAllById(ticketIds); + + if (tickets.isEmpty()) { + throw new TicketNotFoundException(); + } + Map ticketMap = new HashMap<>(tickets.size()); for (var t : tickets) { ticketMap.put(t.getId(), t); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java index b2c6b9ee..e9c4b2a3 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/purchase/usecase/CreatePurchaseUseCaseTest.java @@ -22,6 +22,7 @@ import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.exception.NotEnoughRemainingStockException; +import com.tiketeer.Tiketeer.domain.ticket.exception.TicketNotFoundException; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; @@ -199,4 +200,33 @@ void createPurchaseFailBecauseNotEnoughPoint() { // then }).isInstanceOf(NotEnoughPointException.class); } + + @Test + @DisplayName("구매하고자 하는 티켓이 존재하지 않음 > 구매 요청 > 실패") + @Transactional + void createPurchaseFailBecauseTicketNotFound() { + // given + var sellerEmail = "test@test.com"; + var seller = testHelper.createMember(sellerEmail, RoleEnum.SELLER); + var ticketing = ticketingTestHelper.createTicketing(seller.getId(), 0, 3000, 5); + + var buyerEmail = "test2@test.com"; + var buyer = testHelper.createMember(buyerEmail, RoleEnum.BUYER); + buyer.setPoint(5000); + + var count = 3; + var tickets = List.of( + new CreatePurchaseCommandDto.Ticket(UUID.randomUUID(), count)); + var createPurchaseCommand = CreatePurchaseCommandDto.builder() + .memberEmail(buyerEmail) + .ticketingId(ticketing.getId()) + .tickets(tickets) + .build(); + + Assertions.assertThatThrownBy(() -> { + // when + createPurchaseUseCase.createPurchase(createPurchaseCommand); + // then + }).isInstanceOf(TicketNotFoundException.class); + } } From df3296eaf7c7923ded3c27f07e4358d1036830a7 Mon Sep 17 00:00:00 2001 From: punkryn Date: Mon, 17 Jun 2024 15:56:00 +0900 Subject: [PATCH 28/38] =?UTF-8?q?ticketing=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=9C=A0=EC=A6=88=EC=BC=80=EC=9D=B4=EC=8A=A4=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=97=90=20ticket=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/CreateTicketingUseCaseTest.java | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCaseTest.java index beaba2bd..adfd1511 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/CreateTicketingUseCaseTest.java @@ -71,9 +71,7 @@ void createTicketingFailBecauseInvalidEventTime() { var saleEnd = now.plusYears(2); var eventTime = now.minusYears(20); - var ticketingTickets = createTicketingTickets(); - - var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null, ticketingTickets); + var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null); assertThatThrownBy(() -> { // when @@ -93,8 +91,7 @@ void createTicketingFailBecauseInvalidSaleDuration() { var saleStart = now.plusYears(2); var saleEnd = now.plusYears(1); var eventTime = now.plusYears(3); - var ticketingTickets = createTicketingTickets(); - var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null, ticketingTickets); + var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null); assertThatThrownBy(() -> { // when @@ -114,8 +111,7 @@ void createTicketingFailBecauseEventTimeDuringSaleDuration() { var saleStart = now.plusYears(1); var saleEnd = now.plusYears(3); var eventTime = now.plusYears(2); - var ticketingTickets = createTicketingTickets(); - var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null, ticketingTickets); + var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null); assertThatThrownBy(() -> { // when @@ -134,8 +130,7 @@ void createTicketingFailBecauseInvalidEmail() { var saleStart = now.plusYears(1); var saleEnd = now.plusYears(2); var eventTime = now.plusYears(3); - var ticketingTickets = createTicketingTickets(); - var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null, ticketingTickets); + var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, null); assertThatThrownBy(() -> { // when @@ -155,12 +150,11 @@ void createTicketingSuccess() { var saleStart = now.plusYears(1); var saleEnd = now.plusYears(2); var eventTime = now.plusYears(3); - var ticketingTickets = createTicketingTickets(); MockMultipartFile image = new MockMultipartFile( "thumbnail", "image.png", "image/png", "Image Content".getBytes() ); - var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, image, ticketingTickets); + var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, image); // when var result = createTicketingUseCase.createTicketing(command); @@ -183,8 +177,7 @@ void createTicketingSuccess() { } private CreateTicketingCommandDto createTicketingCommand(String email, LocalDateTime eventTime, - LocalDateTime saleStart, LocalDateTime saleEnd, MultipartFile thumbnail, - List tickets) { + LocalDateTime saleStart, LocalDateTime saleEnd, MultipartFile thumbnail) { return CreateTicketingCommandDto.builder() .memberEmail(email) .title("음악회") @@ -220,13 +213,12 @@ void createTicketingFailInvalidFileType() { var saleStart = now.plusYears(1); var saleEnd = now.plusYears(2); var eventTime = now.plusYears(3); - var ticketingTickets = createTicketingTickets(); MockMultipartFile file = new MockMultipartFile( "thumbnail", "image.txt", "text/plain", "Image Content".getBytes() ); - var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, file, ticketingTickets); + var command = createTicketingCommand(mockEmail, eventTime, saleStart, saleEnd, file); assertThatThrownBy(() -> { // when From 34d8cd54c8ad3e7e454e217fae3f158c32e0a38e Mon Sep 17 00:00:00 2001 From: punkryn Date: Mon, 17 Jun 2024 16:37:45 +0900 Subject: [PATCH 29/38] =?UTF-8?q?ticketing=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/TicketingControllerTest.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java index 9d83041a..e2e37cd7 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java @@ -99,9 +99,7 @@ void getAllTicketingsSuccess() throws Exception { var member = testHelper.createMember("user@example.com", RoleEnum.SELLER); var ticketingCnt = 3; var ticketingsInDb = createTicketings(member, ticketingCnt); - ticketingsInDb.forEach(ticketing -> { - createTickets(ticketing, 3); - }); + ticketingsInDb.forEach(this::createTickets); // when AtomicReference result = new AtomicReference<>(); @@ -140,7 +138,7 @@ void getTicketingSuccess() throws Exception { var member = testHelper.createMember("user@example.com", RoleEnum.SELLER); var ticketingInDb = createTicketings(member, 1).getFirst(); var stock = 10; - createTickets(ticketingInDb, stock); + createTickets(ticketingInDb); // when AtomicReference result = new AtomicReference<>(); @@ -323,10 +321,14 @@ private List createTicketings(Member member, int count) { return ticketingRepository.saveAll(ticketings); } - private List createTickets(Ticketing ticketing, int stock) { - return ticketRepository.saveAll(Arrays.stream(new int[stock]) - .mapToObj(i -> Ticket.builder().ticketing(ticketing).build()) - .toList()); + private List createTickets(Ticketing ticketing) { + return ticketRepository.saveAll( + Arrays.asList( + new Ticket(1000, 10, 10, "Bronze", "", ticketing), + new Ticket(2000, 10, 10, "Silver", "", ticketing), + new Ticket(3000, 10, 10, "Gold", "", ticketing) + ) + ); } private CreateTicketingCommandDto createCreateTicketingCommand(String email, String title, int runningMinutes) { From 492bf52990dcb2e8d45d2cbc906835f6550a2421 Mon Sep 17 00:00:00 2001 From: punkryn Date: Mon, 17 Jun 2024 17:29:02 +0900 Subject: [PATCH 30/38] =?UTF-8?q?=ED=8A=B9=EC=A0=95=20ticketing=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ticketing/usecase/dto/GetTicketing.java | 2 +- .../usecase/GetTicketingUseCaseTest.java | 26 ++++++++----------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/GetTicketing.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/GetTicketing.java index 67ca7ea0..5c6be7b2 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/GetTicketing.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/GetTicketing.java @@ -37,7 +37,7 @@ public interface GetTicketing { @Value("#{target.createdAt}") LocalDateTime getCreatedAt(); - @Value("#{target.owner}") + @Value("#{target.member.email}") String getOwner(); @Value("#{target.thumbnailPath}") diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCaseTest.java index cc36ece3..54689975 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/GetTicketingUseCaseTest.java @@ -62,10 +62,7 @@ void getTicketingSuccess() { var member = testHelper.createMember(mockEmail); var ticketings = createTicketings(member, 1); var ticketing = ticketings.getFirst(); - var stock = 10; - var purchasedStock = 2; - createTickets(ticketing, stock); - createPurchase(member, ticketing, purchasedStock); + createTickets(ticketing); var command = GetTicketingCommandDto.builder().ticketingId(ticketing.getId()).build(); @@ -74,8 +71,8 @@ void getTicketingSuccess() { // then Assertions.assertThat(result.getTitle()).isEqualTo("0"); - // Assertions.assertThat(result.getStock()).isEqualTo(stock); - // Assertions.assertThat(result.getRemainStock()).isEqualTo(stock - purchasedStock); + Assertions.assertThat(result.getTickets().size()).isEqualTo(3); + Assertions.assertThat(result.getTickets().getFirst().getStock()).isEqualTo(10); Assertions.assertThat(result.getOwner()).isEqualTo(member.getEmail()); } @@ -100,15 +97,14 @@ private List createTicketings(Member member, int count) { return ticketingRepository.saveAll(ticketings); } - private List createTickets(Ticketing ticketing, int stock) { - return ticketRepository.saveAll(Arrays.stream(new int[stock]) - .mapToObj(i -> Ticket.builder().ticketing(ticketing).build()) - .toList()); + private List createTickets(Ticketing ticketing) { + return ticketRepository.saveAll( + Arrays.asList( + new Ticket(3000, 10, 10, "Gold", "", ticketing), + new Ticket(2000, 10, 10, "Silver", "", ticketing), + new Ticket(3000, 10, 10, "Bronze", "", ticketing) + ) + ); } - private Purchase createPurchase(Member member, Ticketing ticketing, int count) { - var purchase = purchaseRepository.save(Purchase.builder().member(member).build()); - var tickets = ticketRepository.findByTicketingIdOrderById(ticketing.getId(), Limit.of(count)); - return purchase; - } } \ No newline at end of file From 80fe535dce719644dd1109ae320ba199a5a7d23e Mon Sep 17 00:00:00 2001 From: dla0510 Date: Tue, 18 Jun 2024 13:34:10 +0900 Subject: [PATCH 31/38] =?UTF-8?q?Ticket=20post,=20put=20ep=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/PostPurchaseRequestDto.java | 7 +- .../ticket/controller/TicketController.java | 55 ++++++ .../controller/dto/PostTicketRequestDto.java | 36 ++++ .../controller/dto/PutTicketRequestDto.java | 36 ++++ .../ticket/service/TicketCrudService.java | 12 +- .../ticket/usecase/CreateTicketUseCase.java | 43 +++++ .../ticket/usecase/UpdateTicketUseCase.java | 43 +++++ .../usecase/dto/CreateTicketCommandDto.java | 5 +- .../usecase/dto/CreateTicketMetadata.java | 16 +- .../usecase/dto/UpdateTicketCommandDto.java | 25 +++ .../dto/PostTicketingRequestDto.java | 2 - .../service/TicketingStockService.java | 3 +- .../dto/CreateTicketingCommandDto.java | 3 - .../dto/UpdateTicketingCommandDto.java | 3 - .../controller/TicketControllerTest.java | 161 ++++++++++++++++++ .../ticket/service/TicketCrudServiceTest.java | 32 ++-- .../usecase/CreateTicketUseCaseTest.java | 142 +++++++++++++++ .../usecase/UpdateTicketUseCaseTest.java | 158 +++++++++++++++++ .../controller/TicketingControllerTest.java | 1 - .../service/TicketingStockServiceTest.java | 12 +- .../usecase/DeleteTicketingUseCaseTest.java | 2 - .../usecase/UpdateTicketingUseCaseTest.java | 3 - .../Tiketeer/infra/redis/RedisCacheTest.java | 11 +- 23 files changed, 754 insertions(+), 57 deletions(-) create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketController.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/dto/PostTicketRequestDto.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/dto/PutTicketRequestDto.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/CreateTicketUseCase.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/UpdateTicketUseCase.java create mode 100644 src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/UpdateTicketCommandDto.java create mode 100644 src/test/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketControllerTest.java create mode 100644 src/test/java/com/tiketeer/Tiketeer/domain/ticket/usecase/CreateTicketUseCaseTest.java create mode 100644 src/test/java/com/tiketeer/Tiketeer/domain/ticket/usecase/UpdateTicketUseCaseTest.java diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/PostPurchaseRequestDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/PostPurchaseRequestDto.java index 4adcff79..532a65a7 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/PostPurchaseRequestDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/purchase/controller/dto/PostPurchaseRequestDto.java @@ -25,16 +25,15 @@ public class PostPurchaseRequestDto { @Builder public PostPurchaseRequestDto(@NotNull UUID ticketingId, - @NotNull List tickets) { + @NotNull @Valid List tickets) { this.ticketingId = ticketingId; this.tickets = tickets; - } public CreatePurchaseCommandDto convertToDto(String memberEmail) { return CreatePurchaseCommandDto.builder().memberEmail(memberEmail) - .ticketingId(this.ticketingId) - .tickets(this.tickets) + .ticketingId(ticketingId) + .tickets(tickets) .build(); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketController.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketController.java new file mode 100644 index 00000000..60ed8ef0 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketController.java @@ -0,0 +1,55 @@ +package com.tiketeer.Tiketeer.domain.ticket.controller; + +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.tiketeer.Tiketeer.auth.SecurityContextHelper; +import com.tiketeer.Tiketeer.domain.ticket.controller.dto.PostTicketRequestDto; +import com.tiketeer.Tiketeer.domain.ticket.controller.dto.PutTicketRequestDto; +import com.tiketeer.Tiketeer.domain.ticket.usecase.CreateTicketUseCase; +import com.tiketeer.Tiketeer.domain.ticket.usecase.UpdateTicketUseCase; + +import jakarta.validation.Valid; + +@RestController +@RequestMapping("/ticketing/{ticketingId}/tickets") +public class TicketController { + private final SecurityContextHelper securityContextHelper; + private final CreateTicketUseCase createTicketUseCase; + private final UpdateTicketUseCase updateTicketUseCase; + + @Autowired + public TicketController(SecurityContextHelper securityContextHelper, CreateTicketUseCase createTicketUseCase, + UpdateTicketUseCase updateTicketUseCase) { + this.securityContextHelper = securityContextHelper; + this.createTicketUseCase = createTicketUseCase; + this.updateTicketUseCase = updateTicketUseCase; + } + + @PostMapping + public ResponseEntity postTickets(@PathVariable UUID ticketingId, + @Valid @RequestBody PostTicketRequestDto request) { + var memberEmail = securityContextHelper.getEmailInToken(); + createTicketUseCase.createTicket(request.convertToDto(memberEmail, ticketingId)); + return ResponseEntity.status(HttpStatus.CREATED).build(); + + } + + @PutMapping + public ResponseEntity putTickets(@PathVariable UUID ticketingId, + @Valid @RequestBody PutTicketRequestDto request) { + var memberEmail = securityContextHelper.getEmailInToken(); + updateTicketUseCase.updateTicket(request.convertToDto(memberEmail, ticketingId)); + return ResponseEntity.ok().build(); + } + +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/dto/PostTicketRequestDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/dto/PostTicketRequestDto.java new file mode 100644 index 00000000..2a30ceeb --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/dto/PostTicketRequestDto.java @@ -0,0 +1,36 @@ +package com.tiketeer.Tiketeer.domain.ticket.controller.dto; + +import java.util.List; +import java.util.UUID; + +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketCommandDto; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@ToString +@NoArgsConstructor(force = true) +public class PostTicketRequestDto { + @NotNull + @Valid + private final List tickets; + + @Builder + public PostTicketRequestDto(@NotNull @Valid List tickets) { + this.tickets = tickets; + } + + public CreateTicketCommandDto convertToDto(String memberEmail, UUID ticketingId) { + return CreateTicketCommandDto.builder() + .memberEmail(memberEmail) + .ticketingId(ticketingId) + .createTicketMetadataList(tickets) + .build(); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/dto/PutTicketRequestDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/dto/PutTicketRequestDto.java new file mode 100644 index 00000000..68686fcb --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/dto/PutTicketRequestDto.java @@ -0,0 +1,36 @@ +package com.tiketeer.Tiketeer.domain.ticket.controller.dto; + +import java.util.List; +import java.util.UUID; + +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.UpdateTicketCommandDto; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@ToString +@NoArgsConstructor(force = true) +public class PutTicketRequestDto { + @NotNull + @Valid + private final List tickets; + + @Builder + public PutTicketRequestDto(@NotNull @Valid List tickets) { + this.tickets = tickets; + } + + public UpdateTicketCommandDto convertToDto(String memberEmail, UUID ticketingId) { + return UpdateTicketCommandDto.builder() + .memberEmail(memberEmail) + .ticketingId(ticketingId) + .createTicketMetadataList(tickets) + .build(); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java index 3366409b..da3561b9 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java @@ -9,7 +9,6 @@ import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketCommandDto; import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; @@ -36,10 +35,11 @@ public List listTicketByTicketingId(UUID ticketingId) { } @Transactional - public void createTickets(CreateTicketCommandDto dto) { - var ticketing = findTicketingById(dto.getTicketingId()); + public List createTickets(UUID ticketingId, + List tickets) { + var ticketing = findTicketingById(ticketingId); - ticketRepository.saveAll(dto.getCreateTicketMetadataList().stream() + return ticketRepository.saveAll(tickets.stream() .map(meta -> Ticket.builder() .ticketing(ticketing) .price(meta.getPrice()) @@ -63,12 +63,12 @@ private Ticketing findTicketingById(UUID ticketingId) { } @Transactional - public void updateTickets(UUID ticketingId, List tickets) { + public List updateTickets(UUID ticketingId, List tickets) { var ticketing = findTicketingById(ticketingId); ticketRepository.findAllByTicketing(ticketing) .forEach(ticketRepository::delete); - createTickets(new CreateTicketCommandDto(ticketingId, tickets)); + return createTickets(ticketingId, tickets); } } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/CreateTicketUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/CreateTicketUseCase.java new file mode 100644 index 00000000..180fcbb8 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/CreateTicketUseCase.java @@ -0,0 +1,43 @@ +package com.tiketeer.Tiketeer.domain.ticket.usecase; + +import java.util.Objects; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.tiketeer.Tiketeer.domain.ticket.service.TicketCrudService; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketCommandDto; +import com.tiketeer.Tiketeer.domain.ticketing.exception.ModifyForNotOwnedTicketingException; +import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; +import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class CreateTicketUseCase { + private final TicketCrudService ticketCrudService; + private final TicketingRepository ticketingRepository; + + @Autowired + public CreateTicketUseCase(TicketCrudService ticketCrudService, + TicketingRepository ticketingRepository) { + this.ticketCrudService = ticketCrudService; + this.ticketingRepository = ticketingRepository; + } + + @Transactional + public void createTicket(CreateTicketCommandDto command) { + var email = command.getMemberEmail(); + var ticketingId = command.getTicketingId(); + var ticketMetadataList = command.getCreateTicketMetadataList(); + + var ticketing = ticketingRepository.findById(ticketingId).orElseThrow(TicketingNotFoundException::new); + if (!Objects.equals(email, ticketing.getMember().getEmail())) { + throw new ModifyForNotOwnedTicketingException(); + } + + ticketCrudService.createTickets(ticketingId, ticketMetadataList); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/UpdateTicketUseCase.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/UpdateTicketUseCase.java new file mode 100644 index 00000000..6584a9af --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/UpdateTicketUseCase.java @@ -0,0 +1,43 @@ +package com.tiketeer.Tiketeer.domain.ticket.usecase; + +import java.util.Objects; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.tiketeer.Tiketeer.domain.ticket.service.TicketCrudService; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.UpdateTicketCommandDto; +import com.tiketeer.Tiketeer.domain.ticketing.exception.ModifyForNotOwnedTicketingException; +import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; +import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class UpdateTicketUseCase { + private final TicketCrudService ticketCrudService; + private final TicketingRepository ticketingRepository; + + @Autowired + public UpdateTicketUseCase(TicketCrudService ticketCrudService, + TicketingRepository ticketingRepository) { + this.ticketCrudService = ticketCrudService; + this.ticketingRepository = ticketingRepository; + } + + @Transactional + public void updateTicket(UpdateTicketCommandDto command) { + var email = command.getMemberEmail(); + var ticketingId = command.getTicketingId(); + var ticketMetadataList = command.getCreateTicketMetadataList(); + + var ticketing = ticketingRepository.findById(ticketingId).orElseThrow(TicketingNotFoundException::new); + if (!Objects.equals(email, ticketing.getMember().getEmail())) { + throw new ModifyForNotOwnedTicketingException(); + } + + ticketCrudService.updateTickets(ticketingId, ticketMetadataList); + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketCommandDto.java index 9b5139ff..9cdbf4ab 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketCommandDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketCommandDto.java @@ -10,12 +10,15 @@ @Getter @ToString public class CreateTicketCommandDto { + private final String memberEmail; private final UUID ticketingId; private final List createTicketMetadataList; @Builder - public CreateTicketCommandDto(UUID ticketingId, List createTicketMetadataList) { + public CreateTicketCommandDto(String memberEmail, UUID ticketingId, + List createTicketMetadataList) { + this.memberEmail = memberEmail; this.ticketingId = ticketingId; this.createTicketMetadataList = createTicketMetadataList; } diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketMetadata.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketMetadata.java index edc7c9c1..1eb284eb 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketMetadata.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/CreateTicketMetadata.java @@ -1,5 +1,8 @@ package com.tiketeer.Tiketeer.domain.ticket.usecase.dto; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; import lombok.Builder; import lombok.Getter; import lombok.ToString; @@ -7,15 +10,24 @@ @Getter @ToString public class CreateTicketMetadata { - + @NotNull + @Positive private final int stock; + @NotNull + @Positive private final int remainingStock; + @NotBlank private final String title; private final String description; + @NotNull + @Positive private final long price; @Builder - public CreateTicketMetadata(int stock, int remainingStock, String title, String description, long price) { + public CreateTicketMetadata(@NotNull @Positive int stock, @NotNull @Positive int remainingStock, + @NotBlank String title, + String description, + @NotNull @Positive long price) { this.stock = stock; this.remainingStock = remainingStock; this.title = title; diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/UpdateTicketCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/UpdateTicketCommandDto.java new file mode 100644 index 00000000..aad71654 --- /dev/null +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/usecase/dto/UpdateTicketCommandDto.java @@ -0,0 +1,25 @@ +package com.tiketeer.Tiketeer.domain.ticket.usecase.dto; + +import java.util.List; +import java.util.UUID; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +public class UpdateTicketCommandDto { + private final String memberEmail; + private final UUID ticketingId; + + private final List createTicketMetadataList; + + @Builder + public UpdateTicketCommandDto(String memberEmail, UUID ticketingId, + List createTicketMetadataList) { + this.memberEmail = memberEmail; + this.ticketingId = ticketingId; + this.createTicketMetadataList = createTicketMetadataList; + } +} diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PostTicketingRequestDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PostTicketingRequestDto.java index d97fcaa8..2a828782 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PostTicketingRequestDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/controller/dto/PostTicketingRequestDto.java @@ -1,12 +1,10 @@ package com.tiketeer.Tiketeer.domain.ticketing.controller.dto; import java.time.LocalDateTime; -import java.util.List; import org.springframework.web.multipart.MultipartFile; import com.fasterxml.jackson.annotation.JsonFormat; -import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.usecase.dto.CreateTicketingCommandDto; import jakarta.validation.constraints.NotBlank; diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockService.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockService.java index 885a7946..950efc9e 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockService.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockService.java @@ -9,7 +9,6 @@ import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.service.TicketCrudService; -import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketCommandDto; import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; @Service @@ -27,7 +26,7 @@ public TicketingStockService(TicketCrudService ticketCrudService, TicketingServi @Transactional public void createStock(UUID ticketingId, List tickets) { var ticketing = ticketingService.findById(ticketingId); - ticketCrudService.createTickets(new CreateTicketCommandDto(ticketing.getId(), tickets)); + ticketCrudService.createTickets(ticketing.getId(), tickets); } @Transactional diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/CreateTicketingCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/CreateTicketingCommandDto.java index 9758a7cc..e3f3ea26 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/CreateTicketingCommandDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/CreateTicketingCommandDto.java @@ -1,12 +1,9 @@ package com.tiketeer.Tiketeer.domain.ticketing.usecase.dto; import java.time.LocalDateTime; -import java.util.List; import org.springframework.web.multipart.MultipartFile; -import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; - import lombok.Builder; import lombok.Getter; import lombok.ToString; diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/UpdateTicketingCommandDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/UpdateTicketingCommandDto.java index e16cb338..23bb2015 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/UpdateTicketingCommandDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/dto/UpdateTicketingCommandDto.java @@ -1,13 +1,10 @@ package com.tiketeer.Tiketeer.domain.ticketing.usecase.dto; import java.time.LocalDateTime; -import java.util.List; import java.util.UUID; import org.springframework.web.multipart.MultipartFile; -import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; - import lombok.Builder; import lombok.Getter; import lombok.ToString; diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketControllerTest.java new file mode 100644 index 00000000..fb917669 --- /dev/null +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketControllerTest.java @@ -0,0 +1,161 @@ +package com.tiketeer.Tiketeer.domain.ticket.controller; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.List; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.tiketeer.Tiketeer.auth.constant.JwtMetadata; +import com.tiketeer.Tiketeer.domain.member.exception.MemberNotFoundException; +import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; +import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; +import com.tiketeer.Tiketeer.domain.ticket.controller.dto.PostTicketRequestDto; +import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; +import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; +import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; +import com.tiketeer.Tiketeer.testhelper.TestHelper; + +import jakarta.servlet.http.Cookie; +import lombok.extern.slf4j.Slf4j; + +@Import({TestHelper.class, TicketingTestHelper.class}) +@SpringBootTest +@AutoConfigureMockMvc +@Slf4j +public class TicketControllerTest { + @Autowired + private MockMvc mockMvc; + @Autowired + private TestHelper testHelper; + @Autowired + private TicketingTestHelper ticketingTestHelper; + @Autowired + private TicketingRepository ticketingRepository; + @Autowired + private TicketRepository ticketRepository; + @Autowired + private ObjectMapper objectMapper; + @Autowired + private MemberRepository memberRepository; + + @BeforeEach + void initTable() { + testHelper.initDB(); + } + + @AfterEach + void cleanTable() { + testHelper.cleanDB(); + } + + @Test + @DisplayName("정상 조건 > 티켓 생성 요청 > 성공") + @Transactional + void postTicketSuccess() throws Exception { + // given + var email = "seller@test.com"; + String token = testHelper.registerAndLoginAndReturnAccessToken(email, RoleEnum.SELLER); + var seller = memberRepository.findByEmail(email).orElseThrow(MemberNotFoundException::new); + var ticketing = ticketingTestHelper.createTicketing(seller.getId(), 0); + + Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); + + var ticketStock = 10; + var title = "test"; + var price = 2000; + var tickets = List.of( + CreateTicketMetadata.builder() + .stock(ticketStock) + .remainingStock(ticketStock) + .title(title) + .description("") + .price(price) + .build() + ); + + var req = PostTicketRequestDto.builder() + .tickets(tickets) + .build(); + String content = objectMapper.writeValueAsString(req); + + // when + mockMvc + .perform(post("/api/ticketing/{ticketingId}/tickets", ticketing.getId()) + .contextPath("/api") + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding("utf-8") + .content(content) + .cookie(cookie) + ) + //then + .andExpect(status().isCreated()); + var ticketsInDb = ticketRepository.findAllByTicketing(ticketing); + Assertions.assertThat(ticketsInDb.size()).isEqualTo(1); + Assertions.assertThat(ticketsInDb.getFirst().getTitle()).isEqualTo(title); + Assertions.assertThat(ticketsInDb.getFirst().getStock()).isEqualTo(ticketStock); + Assertions.assertThat(ticketsInDb.getFirst().getPrice()).isEqualTo(price); + } + + @Test + @DisplayName("정상 조건 > 티켓 수정 요청 > 성공") + @Transactional + void putTicketSuccess() throws Exception { + // given + var email = "seller@test.com"; + String token = testHelper.registerAndLoginAndReturnAccessToken(email, RoleEnum.SELLER); + var seller = memberRepository.findByEmail(email).orElseThrow(MemberNotFoundException::new); + var ticketing = ticketingTestHelper.createTicketing(seller.getId(), 0, 1000, 5); + + Cookie cookie = new Cookie(JwtMetadata.ACCESS_TOKEN, token); + + var ticketStock = 10; + var title = "test"; + var price = 2000; + var tickets = List.of( + CreateTicketMetadata.builder() + .stock(ticketStock) + .remainingStock(ticketStock) + .title(title) + .description("") + .price(price) + .build() + ); + + var req = PostTicketRequestDto.builder() + .tickets(tickets) + .build(); + String content = objectMapper.writeValueAsString(req); + + // when + mockMvc + .perform(put("/api/ticketing/{ticketingId}/tickets", ticketing.getId()) + .contextPath("/api") + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding("utf-8") + .content(content) + .cookie(cookie) + ) + //then + .andExpect(status().isOk()); + var ticketsInDb = ticketRepository.findAllByTicketing(ticketing); + Assertions.assertThat(ticketsInDb.size()).isEqualTo(1); + Assertions.assertThat(ticketsInDb.getFirst().getTitle()).isEqualTo(title); + Assertions.assertThat(ticketsInDb.getFirst().getStock()).isEqualTo(ticketStock); + Assertions.assertThat(ticketsInDb.getFirst().getPrice()).isEqualTo(price); + } +} diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java index d0e61bf7..9fb76147 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java @@ -19,7 +19,6 @@ import com.tiketeer.Tiketeer.domain.member.Member; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketCommandDto; import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; @@ -75,16 +74,17 @@ void listTicketByTicketingSuccess() { var mockEmail = "test@test.com"; var member = testHelper.createMember(mockEmail); + var mockTicket = 5; var mockStock = 30; var ticketing = createTicketing(member, now.plusYears(1), now.plusYears(2), now.plusYears(3)); - createTickets(ticketing, 5, mockStock, mockStock, "", "", 1000); + createTickets(ticketing, mockTicket, mockStock, mockStock, "", "", 1000); // when var tickets = ticketCrudService.listTicketByTicketingId(ticketing.getId()); // then - Assertions.assertThat(tickets.size()).isEqualTo(mockStock); + Assertions.assertThat(tickets.size()).isEqualTo(mockTicket); } @Test @@ -95,10 +95,7 @@ void createTicketsFailBecauseNotExistTicketing() { Assertions.assertThatThrownBy(() -> { // when - ticketCrudService.createTickets(CreateTicketCommandDto.builder() - .ticketingId(invalidTicketingId) - .createTicketMetadataList(new ArrayList<>()) - .build()); + ticketCrudService.createTickets(invalidTicketingId, new ArrayList<>()); // then }).isInstanceOf(TicketingNotFoundException.class); } @@ -119,18 +116,15 @@ void createTicketsSuccess() { var addTickets = 20; // when - ticketCrudService.createTickets(CreateTicketCommandDto.builder() - .ticketingId(ticketing.getId()) - .createTicketMetadataList(Collections.singletonList( - CreateTicketMetadata.builder() - .stock(addTickets) - .remainingStock(addTickets) - .title("") - .description("") - .price(2000) - .build() - )) - .build()); + ticketCrudService.createTickets(ticketing.getId(), Collections.singletonList( + CreateTicketMetadata.builder() + .stock(addTickets) + .remainingStock(addTickets) + .title("") + .description("") + .price(2000) + .build() + )); // then var tickets = ticketCrudService.listTicketByTicketingId(ticketing.getId()); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/usecase/CreateTicketUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/usecase/CreateTicketUseCaseTest.java new file mode 100644 index 00000000..4e36f421 --- /dev/null +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/usecase/CreateTicketUseCaseTest.java @@ -0,0 +1,142 @@ +package com.tiketeer.Tiketeer.domain.ticket.usecase; + +import java.util.List; +import java.util.UUID; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; + +import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; +import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketCommandDto; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; +import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; +import com.tiketeer.Tiketeer.domain.ticketing.exception.ModifyForNotOwnedTicketingException; +import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; +import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; +import com.tiketeer.Tiketeer.testhelper.TestHelper; + +@Import({TestHelper.class, TicketingTestHelper.class}) +@SpringBootTest +public class CreateTicketUseCaseTest { + @Autowired + private TestHelper testHelper; + @Autowired + private TicketingTestHelper ticketingTestHelper; + @Autowired + private TicketingRepository ticketingRepository; + @Autowired + private TicketRepository ticketRepository; + @Autowired + private MemberRepository memberRepository; + @Autowired + private CreateTicketUseCase createTicketUseCase; + + @BeforeEach + void initTable() { + testHelper.initDB(); + } + + @AfterEach + void cleanTable() { + testHelper.cleanDB(); + } + + @Test + @DisplayName("정상 조건 > 티켓 추가 요청 > 성공") + void createTicketSuccess() { + // given + var email = "test@test.com"; + var member = testHelper.createMember(email); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 1); + var tickets = List.of( + CreateTicketMetadata.builder() + .stock(10) + .remainingStock(10) + .title("test") + .description("") + .price(2000) + .build() + ); + + // when + var command = CreateTicketCommandDto.builder() + .memberEmail(email) + .ticketingId(ticketing.getId()) + .createTicketMetadataList(tickets) + .build(); + createTicketUseCase.createTicket(command); + + // then + var ticketsInDb = ticketRepository.findAllByTicketing(ticketing); + Assertions.assertThat(ticketsInDb.size()).isEqualTo(1); + Assertions.assertThat(ticketsInDb.getFirst().getTitle()).isEqualTo("test"); + Assertions.assertThat(ticketsInDb.getFirst().getStock()).isEqualTo(10); + Assertions.assertThat(ticketsInDb.getFirst().getPrice()).isEqualTo(2000); + } + + @Test + @DisplayName("존재하지 않는 티케팅 > 티켓 추가 요청 > 실패") + void createTicketFailBecauseTicketingNotExist() { + // given + var email = "test@test.com"; + var member = testHelper.createMember(email); + var tickets = List.of( + CreateTicketMetadata.builder() + .stock(10) + .remainingStock(10) + .title("test") + .description("") + .price(2000) + .build() + ); + + var command = CreateTicketCommandDto.builder() + .memberEmail(email) + .ticketingId(UUID.randomUUID()) + .createTicketMetadataList(tickets) + .build(); + Assertions.assertThatThrownBy(() -> { + // when + createTicketUseCase.createTicket(command); + //then + }).isInstanceOf(TicketingNotFoundException.class); + } + + @Test + @DisplayName("티케팅 owner가 아님 > 티켓 추가 요청 > 실패") + void createTicketFailBecauseNotOwnedTicketing() { + // given + var email = "test@test.com"; + var member = testHelper.createMember(email); + var ticketingOwner = testHelper.createMember("owner@test.com"); + var ticketing = ticketingTestHelper.createTicketing(ticketingOwner.getId(), 1); + var tickets = List.of( + CreateTicketMetadata.builder() + .stock(10) + .remainingStock(10) + .title("test") + .description("") + .price(2000) + .build() + ); + + // when + var command = CreateTicketCommandDto.builder() + .memberEmail(email) + .ticketingId(ticketing.getId()) + .createTicketMetadataList(tickets) + .build(); + Assertions.assertThatThrownBy(() -> { + // when + createTicketUseCase.createTicket(command); + // then + }).isInstanceOf(ModifyForNotOwnedTicketingException.class); + } +} diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/usecase/UpdateTicketUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/usecase/UpdateTicketUseCaseTest.java new file mode 100644 index 00000000..bdc3f83e --- /dev/null +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/usecase/UpdateTicketUseCaseTest.java @@ -0,0 +1,158 @@ +package com.tiketeer.Tiketeer.domain.ticket.usecase; + +import java.util.List; +import java.util.UUID; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; + +import com.tiketeer.Tiketeer.domain.member.repository.MemberRepository; +import com.tiketeer.Tiketeer.domain.ticket.Ticket; +import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; +import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.UpdateTicketCommandDto; +import com.tiketeer.Tiketeer.domain.ticketing.TicketingTestHelper; +import com.tiketeer.Tiketeer.domain.ticketing.exception.ModifyForNotOwnedTicketingException; +import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; +import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; +import com.tiketeer.Tiketeer.testhelper.TestHelper; + +@Import({TestHelper.class, TicketingTestHelper.class}) +@SpringBootTest +public class UpdateTicketUseCaseTest { + @Autowired + private TestHelper testHelper; + @Autowired + private TicketingTestHelper ticketingTestHelper; + @Autowired + private TicketingRepository ticketingRepository; + @Autowired + private TicketRepository ticketRepository; + @Autowired + private MemberRepository memberRepository; + @Autowired + private UpdateTicketUseCase updateTicketUseCase; + + @BeforeEach + void initTable() { + testHelper.initDB(); + } + + @AfterEach + void cleanTable() { + testHelper.cleanDB(); + } + + @Test + @DisplayName("정상 조건 > 티켓 수정 요청 > 성공") + void createTicketSuccess() { + // given + var email = "test@test.com"; + var member = testHelper.createMember(email); + var ticketing = ticketingTestHelper.createTicketing(member.getId(), 1); + var ticket1 = ticketRepository.save(new Ticket(1000, 10, 10, "ticket1", "", ticketing)); + var ticket2 = ticketRepository.save(new Ticket(1000, 10, 10, "ticket2", "", ticketing)); + + // when + var newTicket = new Ticket(500, 1, 1, "ticket3", "", ticketing); + var tickets = List.of( + CreateTicketMetadata.builder() + .stock(ticket1.getStock()) + .remainingStock(ticket1.getRemainingStock()) + .title(ticket1.getTitle()) + .description(ticket1.getDescription()) + .price(ticket1.getPrice()) + .build(), + CreateTicketMetadata.builder() + .stock(newTicket.getStock()) + .remainingStock(newTicket.getRemainingStock()) + .title(newTicket.getTitle()) + .price(newTicket.getPrice()) + .build() + ); + var command = UpdateTicketCommandDto.builder() + .memberEmail(email) + .ticketingId(ticketing.getId()) + .createTicketMetadataList(tickets) + .build(); + updateTicketUseCase.updateTicket(command); + + // then + var ticketsInDb = ticketRepository.findAllByTicketing(ticketing); + Assertions.assertThat(ticketsInDb.size()).isEqualTo(2); + Assertions.assertThat(ticketsInDb) + .extracting("title") + .containsExactlyInAnyOrder(ticket1.getTitle(), newTicket.getTitle()); + Assertions.assertThat(ticketsInDb) + .extracting("stock") + .containsExactlyInAnyOrder(ticket1.getStock(), newTicket.getStock()); + Assertions.assertThat(ticketsInDb) + .extracting("price") + .containsExactlyInAnyOrder(ticket1.getPrice(), newTicket.getPrice()); + } + + @Test + @DisplayName("존재하지 않는 티케팅 > 티켓 수정 요청 > 실패") + void createTicketFailBecauseTicketingNotExist() { + // given + var email = "test@test.com"; + var member = testHelper.createMember(email); + var tickets = List.of( + CreateTicketMetadata.builder() + .stock(10) + .remainingStock(10) + .title("test") + .description("") + .price(2000) + .build() + ); + + var command = UpdateTicketCommandDto.builder() + .memberEmail(email) + .ticketingId(UUID.randomUUID()) + .createTicketMetadataList(tickets) + .build(); + Assertions.assertThatThrownBy(() -> { + // when + updateTicketUseCase.updateTicket(command); + //then + }).isInstanceOf(TicketingNotFoundException.class); + } + + @Test + @DisplayName("티케팅 owner가 아님 > 티켓 수정 요청 > 실패") + void createTicketFailBecauseNotOwnedTicketing() { + // given + var email = "test@test.com"; + var member = testHelper.createMember(email); + var ticketingOwner = testHelper.createMember("owner@test.com"); + var ticketing = ticketingTestHelper.createTicketing(ticketingOwner.getId(), 1); + var tickets = List.of( + CreateTicketMetadata.builder() + .stock(10) + .remainingStock(10) + .title("test") + .description("") + .price(2000) + .build() + ); + + // when + var command = UpdateTicketCommandDto.builder() + .memberEmail(email) + .ticketingId(ticketing.getId()) + .createTicketMetadataList(tickets) + .build(); + Assertions.assertThatThrownBy(() -> { + // when + updateTicketUseCase.updateTicket(command); + // then + }).isInstanceOf(ModifyForNotOwnedTicketingException.class); + } +} diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java index 9d83041a..54ee8485 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/controller/TicketingControllerTest.java @@ -40,7 +40,6 @@ import com.tiketeer.Tiketeer.domain.role.constant.RoleEnum; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.Ticketing; import com.tiketeer.Tiketeer.domain.ticketing.controller.dto.GetAllTicketingsResponseDto; import com.tiketeer.Tiketeer.domain.ticketing.controller.dto.GetTicketingResponseDto; diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockServiceTest.java index 7a38e8bf..323cf016 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/service/TicketingStockServiceTest.java @@ -111,13 +111,16 @@ void updateStockSuccessForAddRequest() { var ticketing = createTicketing(member); var initStock = 10; - ticketingStockService.createStock(ticketing.getId(), List.of(new CreateTicketMetadata(initStock, initStock, "Gold", "", 1000))); + ticketingStockService.createStock(ticketing.getId(), + List.of(new CreateTicketMetadata(initStock, initStock, "Gold", "", 1000))); - Assertions.assertThat(ticketRepository.findAllByTicketing(ticketing).getFirst().getStock()).isEqualTo(initStock); + Assertions.assertThat(ticketRepository.findAllByTicketing(ticketing).getFirst().getStock()) + .isEqualTo(initStock); // when var updateStock = 20; - ticketingStockService.updateStock(ticketing.getId(), List.of(new CreateTicketMetadata(updateStock, updateStock, "Gold", "", 1000))); + ticketingStockService.updateStock(ticketing.getId(), + List.of(new CreateTicketMetadata(updateStock, updateStock, "Gold", "", 1000))); // then var tickets = ticketRepository.findAllByTicketing(ticketing); @@ -134,7 +137,8 @@ void updateStockSuccessForRemoveRequest() { var ticketing = createTicketing(member); var initStock = 30; - ticketingStockService.createStock(ticketing.getId(), List.of(new CreateTicketMetadata(initStock, initStock, "Gold", "", 1000))); + ticketingStockService.createStock(ticketing.getId(), + List.of(new CreateTicketMetadata(initStock, initStock, "Gold", "", 1000))); Assertions.assertThat(ticketRepository.findAllByTicketing(ticketing).size()).isEqualTo(1); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/DeleteTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/DeleteTicketingUseCaseTest.java index 23e8607e..8d3de22b 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/DeleteTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/DeleteTicketingUseCaseTest.java @@ -1,7 +1,6 @@ package com.tiketeer.Tiketeer.domain.ticketing.usecase; import java.time.LocalDateTime; -import java.util.Arrays; import java.util.UUID; import org.assertj.core.api.Assertions; @@ -15,7 +14,6 @@ import org.springframework.transaction.annotation.Transactional; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.exception.DeleteTicketingAfterSaleStartException; import com.tiketeer.Tiketeer.domain.ticketing.exception.ModifyForNotOwnedTicketingException; import com.tiketeer.Tiketeer.domain.ticketing.exception.TicketingNotFoundException; diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java index d635698b..561a1835 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/usecase/UpdateTicketingUseCaseTest.java @@ -2,7 +2,6 @@ import java.nio.file.Path; import java.time.LocalDateTime; -import java.util.Arrays; import java.util.UUID; import org.assertj.core.api.Assertions; @@ -18,9 +17,7 @@ import org.springframework.context.annotation.Import; import org.springframework.transaction.annotation.Transactional; -import com.tiketeer.Tiketeer.configuration.EmbeddedRedisConfig; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; -import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.exception.EventTimeNotValidException; import com.tiketeer.Tiketeer.domain.ticketing.exception.ModifyForNotOwnedTicketingException; import com.tiketeer.Tiketeer.domain.ticketing.exception.SaleDurationNotValidException; diff --git a/src/test/java/com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java b/src/test/java/com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java index e95d71c2..57ac7401 100644 --- a/src/test/java/com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/infra/redis/RedisCacheTest.java @@ -3,7 +3,6 @@ import static org.assertj.core.api.Assertions.*; import java.time.LocalDateTime; -import java.util.Arrays; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -16,7 +15,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.tiketeer.Tiketeer.domain.ticket.usecase.dto.CreateTicketMetadata; import com.tiketeer.Tiketeer.domain.ticketing.repository.TicketingRepository; import com.tiketeer.Tiketeer.domain.ticketing.usecase.CreateTicketingUseCase; import com.tiketeer.Tiketeer.domain.ticketing.usecase.DeleteTicketingUseCase; @@ -79,7 +77,6 @@ void cacheUpdateTest() throws JsonProcessingException { CreateTicketingResultDto ticketing = createTicketing(email, saleStart, saleEnd); - LocalDateTime changedSaleStart = LocalDateTime.now().plusDays(10); LocalDateTime changedSaleEnd = LocalDateTime.now().plusDays(20); @@ -89,7 +86,9 @@ void cacheUpdateTest() throws JsonProcessingException { .ticketingId(ticketing.getTicketingId()) .build(); - redisTemplate.opsForValue().set("ticketingId::" + ticketing.getTicketingId().toString(), objectMapper.writeValueAsString(updateTicketingSalePeriodDto)); + redisTemplate.opsForValue() + .set("ticketingId::" + ticketing.getTicketingId().toString(), + objectMapper.writeValueAsString(updateTicketingSalePeriodDto)); // when UpdateTicketingCommandDto updateTicketingCommandDto = UpdateTicketingCommandDto.builder() @@ -130,7 +129,9 @@ void cacheDeleteTest() throws JsonProcessingException { .ticketingId(ticketing.getTicketingId()) .build(); - redisTemplate.opsForValue().set("ticketingId::" + ticketing.getTicketingId().toString(), objectMapper.writeValueAsString(updateTicketingSalePeriodDto)); + redisTemplate.opsForValue() + .set("ticketingId::" + ticketing.getTicketingId().toString(), + objectMapper.writeValueAsString(updateTicketingSalePeriodDto)); // when deleteTicketingUseCase.deleteTicketing(DeleteTicketingCommandDto.builder() From 35f77726937a1fb39decd4bd7b778c4d3fce4d9f Mon Sep 17 00:00:00 2001 From: dla0510 Date: Tue, 18 Jun 2024 13:34:38 +0900 Subject: [PATCH 32/38] =?UTF-8?q?test=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/ticket/repository/TicketRepository.java | 8 -------- .../domain/ticket/repository/TicketRepositoryTest.java | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java index 4142305f..72f27172 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepository.java @@ -20,14 +20,6 @@ public interface TicketRepository extends JpaRepository { List findByTicketingIdOrderById(UUID ticketingId, Limit limit); - @Lock(LockModeType.PESSIMISTIC_WRITE) - @Query("SELECT t FROM Ticket t WHERE t.ticketing.id = :ticketingId ORDER BY t.id") - List findByTicketingIdOrderByIdWithPessimisticLock(UUID ticketingId, Limit limit); - - @Lock(LockModeType.OPTIMISTIC) - @Query("SELECT t FROM Ticket t WHERE t.ticketing.id = :ticketingId ORDER BY FUNCTION('RAND')") - List findByTicketingIdOrderByIdWithOptimisticLock(UUID ticketingId, Limit limit); - @Lock(LockModeType.PESSIMISTIC_WRITE) @Query("SELECT t FROM Ticket t WHERE t.id IN :ticketIds") List findAllByIdWithPessimisticLock(List ticketIds); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepositoryTest.java index a5ece237..fe23c1e8 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepositoryTest.java @@ -59,9 +59,9 @@ void findByTicketingIdAndPurchaseIsNullOrderByIdWithLimit() { .build() ); ticketRepository.save( - Ticket.builder().ticketing(ticketing).build()); + Ticket.builder().ticketing(ticketing).title("ticket1").price(1000).stock(10).remainingStock(10).build()); ticketRepository.save( - Ticket.builder().ticketing(ticketing).build()); + Ticket.builder().ticketing(ticketing).title("ticket2").price(1000).stock(10).remainingStock(10).build()); // when var tickets = ticketRepository.findByTicketingIdOrderById(ticketing.getId(), Limit.of(1)); From 9d1f2727754df7fa0e8475f0ba3c33aa5c273c7a Mon Sep 17 00:00:00 2001 From: punkryn Date: Tue, 18 Jun 2024 14:34:16 +0900 Subject: [PATCH 33/38] =?UTF-8?q?ticketing=20=ED=8C=90=EB=A7=A4=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=A1=B0=ED=9A=8C=20EP=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/GetMemberTicketingSalesResultDto.java | 12 +++---- .../repository/TicketingRepository.java | 2 +- .../controller/MemberControllerTest.java | 32 +++++++++++++---- .../GetMemberTicketingSaleUseCaseTest.java | 35 +++++++++++++++---- .../repository/TicketRepositoryTest.java | 6 ++-- .../ticket/service/TicketCrudServiceTest.java | 2 +- .../repository/TicketingRepositoryTest.java | 33 ++++++++++++++--- 7 files changed, 96 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberTicketingSalesResultDto.java b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberTicketingSalesResultDto.java index 4ecd6762..41ca10d5 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberTicketingSalesResultDto.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/member/usecase/dto/GetMemberTicketingSalesResultDto.java @@ -19,12 +19,12 @@ public class GetMemberTicketingSalesResultDto { private final LocalDateTime eventTime; private final LocalDateTime saleStart; private final LocalDateTime saleEnd; - private final int stock; - private final int remainStock; + private final Long stock; + private final Long remainStock; private final LocalDateTime createdAt; private final String category; private final int runningMinutes; - private final long price; + private final Long price; @Builder public GetMemberTicketingSalesResultDto( @@ -35,12 +35,12 @@ public GetMemberTicketingSalesResultDto( LocalDateTime eventTime, LocalDateTime saleStart, LocalDateTime saleEnd, - int stock, - int remainStock, + Long stock, + Long remainStock, LocalDateTime createdAt, String category, int runningMinutes, - long price + Long price ) { this.ticketingId = ticketingId; this.title = title; diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepository.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepository.java index d0eb35df..6edb1003 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepository.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepository.java @@ -23,7 +23,7 @@ public interface TicketingRepository extends JpaRepository { (select SUM(tk.stock) from Ticket tk where tk.ticketing = t), (select SUM(tk.remainingStock) from Ticket tk where tk.ticketing = t), t.createdAt, t.category, t.runningMinutes, - (select SUM(tk.price) from Ticket tk where tk.ticketing = t) + (select SUM(tk.price) from Ticket tk JOIN PurchaseItem pi ON pi.ticket = tk where tk.ticketing = t) ) from Ticketing t where t.member.email = :email diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java index 9d4bc6b5..1656d68f 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java @@ -8,6 +8,7 @@ import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; +import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -105,9 +106,28 @@ void getMemberTicketingSalesSuccess() throws Exception { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing)); + + var t1 = new Ticket(1000, 10, 7, "title 1", "description 1", ticketing); + var t2 = new Ticket(2000, 10, 7, "title 2", "description 1", ticketing); + var t3 = new Ticket(3000, 10, 9, "title 3", "description 1", ticketing); + ticketRepository.save(t1); + ticketRepository.save(t2); + ticketRepository.save(t3); + + var t1Count = 3; + purchaseItemRepository.saveAll(Arrays.stream(new int[t1Count]) + .mapToObj(i -> new PurchaseItem(purchase, t1)) + .toList()); + var t2Count = 3; + purchaseItemRepository.saveAll(Arrays.stream(new int[t2Count]) + .mapToObj(i -> new PurchaseItem(purchase, t2)) + .toList() + ); + var t3Count = 1; + purchaseItemRepository.saveAll(Arrays.stream(new int[t3Count]) + .mapToObj(i -> new PurchaseItem(purchase, t3)) + .toList() + ); //when - then MvcResult result = mockMvc.perform(get("/api/members/" + member.getId() + "/sale") @@ -127,15 +147,15 @@ void getMemberTicketingSalesSuccess() throws Exception { var dto = apiResponse.getData().getFirst(); - assertThat(dto.getPrice()).isEqualTo(1000); + assertThat(dto.getPrice()).isEqualTo(12000); assertThat(dto.getDescription()).isEqualTo(""); assertThat(dto.getTitle()).isEqualTo("test"); assertThat(dto.getLocation()).isEqualTo("Seoul"); assertThat(dto.getEventTime()).isEqualToIgnoringNanos(now); assertThat(dto.getSaleStart()).isEqualToIgnoringNanos(now); assertThat(dto.getSaleEnd()).isEqualToIgnoringNanos(now); - assertThat(dto.getStock()).isEqualTo(3); - assertThat(dto.getRemainStock()).isEqualTo(1); + assertThat(dto.getStock()).isEqualTo(30); + assertThat(dto.getRemainStock()).isEqualTo(23); assertThat(dto.getCategory()).isEqualTo(""); assertThat(dto.getRunningMinutes()).isEqualTo(600); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberTicketingSaleUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberTicketingSaleUseCaseTest.java index c9addef2..bfa18c3f 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberTicketingSaleUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberTicketingSaleUseCaseTest.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.*; import java.time.temporal.ChronoUnit; +import java.util.Arrays; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -15,6 +16,8 @@ import com.tiketeer.Tiketeer.domain.member.usecase.dto.GetMemberTicketingSalesCommandDto; import com.tiketeer.Tiketeer.domain.purchase.Purchase; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; @@ -36,6 +39,8 @@ class GetMemberTicketingSaleUseCaseTest { private TicketRepository ticketRepository; @Autowired private GetMemberTicketingSalesUseCase getMemberTicketingSalesUseCase; + @Autowired + private PurchaseItemRepository purchaseItemRepository; @BeforeEach void initTable() { @@ -68,9 +73,27 @@ void getMemberTicketingSalesSuccess() { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing)); + var t1 = new Ticket(1000, 10, 7, "title 1", "description 1", ticketing); + var t2 = new Ticket(2000, 10, 7, "title 2", "description 1", ticketing); + var t3 = new Ticket(3000, 10, 9, "title 3", "description 1", ticketing); + ticketRepository.save(t1); + ticketRepository.save(t2); + ticketRepository.save(t3); + + var t1Count = 3; + purchaseItemRepository.saveAll(Arrays.stream(new int[t1Count]) + .mapToObj(i -> new PurchaseItem(purchase, t1)) + .toList()); + var t2Count = 3; + purchaseItemRepository.saveAll(Arrays.stream(new int[t2Count]) + .mapToObj(i -> new PurchaseItem(purchase, t2)) + .toList() + ); + var t3Count = 1; + purchaseItemRepository.saveAll(Arrays.stream(new int[t3Count]) + .mapToObj(i -> new PurchaseItem(purchase, t3)) + .toList() + ); //when var memberTicketingSale = getMemberTicketingSalesUseCase.getMemberTicketingSales( @@ -83,8 +106,8 @@ void getMemberTicketingSalesSuccess() { assertThat(memberTicketingSale.getDescription()).isEqualTo(""); assertThat(memberTicketingSale.getSaleStart()).isEqualTo(now); assertThat(memberTicketingSale.getSaleEnd()).isEqualTo(now); - assertThat(memberTicketingSale.getRemainStock()).isEqualTo(1); - assertThat(memberTicketingSale.getStock()).isEqualTo(3); - + assertThat(memberTicketingSale.getRemainStock()).isEqualTo(23); + assertThat(memberTicketingSale.getStock()).isEqualTo(30); + assertThat(memberTicketingSale.getPrice()).isEqualTo(12000); } } \ No newline at end of file diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepositoryTest.java index a5ece237..3b6bdf66 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/repository/TicketRepositoryTest.java @@ -59,9 +59,11 @@ void findByTicketingIdAndPurchaseIsNullOrderByIdWithLimit() { .build() ); ticketRepository.save( - Ticket.builder().ticketing(ticketing).build()); + Ticket.builder().ticketing(ticketing).title("Gold").description("").price(3000).stock(10).remainingStock(10).build()); ticketRepository.save( - Ticket.builder().ticketing(ticketing).build()); + Ticket.builder().ticketing(ticketing).title("Silver").description("").price(2000).stock(10).remainingStock(10).build()); + ticketRepository.save( + Ticket.builder().ticketing(ticketing).title("Bronze").description("").price(1000).stock(10).remainingStock(10).build()); // when var tickets = ticketRepository.findByTicketingIdOrderById(ticketing.getId(), Limit.of(1)); diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java index d0e61bf7..f1328e3d 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudServiceTest.java @@ -84,7 +84,7 @@ void listTicketByTicketingSuccess() { var tickets = ticketCrudService.listTicketByTicketingId(ticketing.getId()); // then - Assertions.assertThat(tickets.size()).isEqualTo(mockStock); + Assertions.assertThat(tickets.size()).isEqualTo(5); } @Test diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java index 5913f3ee..b5c5fe07 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticketing/repository/TicketingRepositoryTest.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.*; import java.time.temporal.ChronoUnit; +import java.util.Arrays; import java.util.Objects; import org.junit.jupiter.api.AfterEach; @@ -15,6 +16,8 @@ import org.springframework.context.annotation.Import; import com.tiketeer.Tiketeer.domain.purchase.Purchase; +import com.tiketeer.Tiketeer.domain.purchase.PurchaseItem; +import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseItemRepository; import com.tiketeer.Tiketeer.domain.purchase.repository.PurchaseRepository; import com.tiketeer.Tiketeer.domain.ticket.Ticket; import com.tiketeer.Tiketeer.domain.ticket.repository.TicketRepository; @@ -33,6 +36,8 @@ class TicketingRepositoryTest { private PurchaseRepository purchaseRepository; @Autowired private TicketingRepository ticketingRepository; + @Autowired + private PurchaseItemRepository purchaseItemRepository; @BeforeEach void initTable() { @@ -65,9 +70,29 @@ void findTicketingWithTicketStock() { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - ticketRepository.save(new Ticket(1000, 1, 1, "title 1", "description 1", ticketing)); - ticketRepository.save(new Ticket(2000, 2, 2, "title 2", "description 1", ticketing)); - ticketRepository.save(new Ticket(3000, 3, 3, "title 3", "description 1", ticketing)); + + var t1 = new Ticket(1000, 1, 1, "title 1", "description 1", ticketing); + var t2 = new Ticket(2000, 2, 2, "title 2", "description 1", ticketing); + var t3 = new Ticket(3000, 3, 3, "title 3", "description 1", ticketing); + ticketRepository.save(t1); + ticketRepository.save(t2); + ticketRepository.save(t3); + + var t1Count = 3; + purchaseItemRepository.saveAll(Arrays.stream(new int[t1Count]) + .mapToObj(i -> new PurchaseItem(purchase, t1)) + .toList()); + var t2Count = 3; + purchaseItemRepository.saveAll(Arrays.stream(new int[t2Count]) + .mapToObj(i -> new PurchaseItem(purchase, t2)) + .toList() + ); + var t3Count = 1; + purchaseItemRepository.saveAll(Arrays.stream(new int[t3Count]) + .mapToObj(i -> new PurchaseItem(purchase, t3)) + .toList() + ); + //when var memberTicketingSale = ticketingRepository.findTicketingWithTicketStock(member.getEmail()).getFirst(); @@ -81,7 +106,7 @@ void findTicketingWithTicketStock() { assertThat(memberTicketingSale.getRemainStock()).isEqualTo(6); assertThat(memberTicketingSale.getStock()).isEqualTo(6); - assertThat(memberTicketingSale.getPrice()).isEqualTo(6000); + assertThat(memberTicketingSale.getPrice()).isEqualTo(12000); } From e1735ad65650e2da9c4ab82b7d0bf5656a8c503c Mon Sep 17 00:00:00 2001 From: dla0510 Date: Tue, 18 Jun 2024 16:29:31 +0900 Subject: [PATCH 34/38] =?UTF-8?q?deleteAllInBatch=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Tiketeer/domain/ticket/service/TicketCrudService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java index da3561b9..acb57d78 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/service/TicketCrudService.java @@ -66,8 +66,8 @@ private Ticketing findTicketingById(UUID ticketingId) { public List updateTickets(UUID ticketingId, List tickets) { var ticketing = findTicketingById(ticketingId); - ticketRepository.findAllByTicketing(ticketing) - .forEach(ticketRepository::delete); + var ticketsInDb = ticketRepository.findAllByTicketing(ticketing); + ticketRepository.deleteAllInBatch(ticketsInDb); return createTickets(ticketingId, tickets); } From bbbf4d06c9b968a8e8fd83282775e3e1a807640c Mon Sep 17 00:00:00 2001 From: punkryn Date: Tue, 18 Jun 2024 17:09:33 +0900 Subject: [PATCH 35/38] =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/controller/MemberControllerTest.java | 9 +++------ .../usecase/GetMemberTicketingSaleUseCaseTest.java | 9 +++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java index 1656d68f..59305224 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/controller/MemberControllerTest.java @@ -107,12 +107,9 @@ void getMemberTicketingSalesSuccess() throws Exception { ); var purchase = purchaseRepository.save(new Purchase(member)); - var t1 = new Ticket(1000, 10, 7, "title 1", "description 1", ticketing); - var t2 = new Ticket(2000, 10, 7, "title 2", "description 1", ticketing); - var t3 = new Ticket(3000, 10, 9, "title 3", "description 1", ticketing); - ticketRepository.save(t1); - ticketRepository.save(t2); - ticketRepository.save(t3); + var t1= ticketRepository.save(new Ticket(1000, 10, 7, "title 1", "description 1", ticketing)); + var t2 = ticketRepository.save(new Ticket(2000, 10, 7, "title 2", "description 1", ticketing)); + var t3 = ticketRepository.save(new Ticket(3000, 10, 9, "title 3", "description 1", ticketing)); var t1Count = 3; purchaseItemRepository.saveAll(Arrays.stream(new int[t1Count]) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberTicketingSaleUseCaseTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberTicketingSaleUseCaseTest.java index bfa18c3f..cc42b8b5 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberTicketingSaleUseCaseTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/member/usecase/GetMemberTicketingSaleUseCaseTest.java @@ -73,12 +73,9 @@ void getMemberTicketingSalesSuccess() { .build() ); var purchase = purchaseRepository.save(new Purchase(member)); - var t1 = new Ticket(1000, 10, 7, "title 1", "description 1", ticketing); - var t2 = new Ticket(2000, 10, 7, "title 2", "description 1", ticketing); - var t3 = new Ticket(3000, 10, 9, "title 3", "description 1", ticketing); - ticketRepository.save(t1); - ticketRepository.save(t2); - ticketRepository.save(t3); + var t1= ticketRepository.save(new Ticket(1000, 10, 7, "title 1", "description 1", ticketing)); + var t2 = ticketRepository.save(new Ticket(2000, 10, 7, "title 2", "description 1", ticketing)); + var t3 = ticketRepository.save(new Ticket(3000, 10, 9, "title 3", "description 1", ticketing)); var t1Count = 3; purchaseItemRepository.saveAll(Arrays.stream(new int[t1Count]) From 72bb38e64478af2af2e348e60fe43b2fc0cd3196 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Fri, 21 Jun 2024 16:54:37 +0900 Subject: [PATCH 36/38] =?UTF-8?q?ticket=20ep=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Tiketeer/domain/ticket/controller/TicketController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketController.java b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketController.java index 60ed8ef0..4acb7852 100644 --- a/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketController.java +++ b/src/main/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketController.java @@ -21,7 +21,7 @@ import jakarta.validation.Valid; @RestController -@RequestMapping("/ticketing/{ticketingId}/tickets") +@RequestMapping("/ticketings/{ticketingId}/tickets") public class TicketController { private final SecurityContextHelper securityContextHelper; private final CreateTicketUseCase createTicketUseCase; From 7b554677c5fff4aae90a5a1698b5ca0ab8f45b4b Mon Sep 17 00:00:00 2001 From: dla0510 Date: Fri, 21 Jun 2024 17:01:42 +0900 Subject: [PATCH 37/38] =?UTF-8?q?security=20=EA=B6=8C=ED=95=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/tiketeer/Tiketeer/auth/RequestMatcherHolder.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/tiketeer/Tiketeer/auth/RequestMatcherHolder.java b/src/main/java/com/tiketeer/Tiketeer/auth/RequestMatcherHolder.java index ca071aa0..a92559a2 100644 --- a/src/main/java/com/tiketeer/Tiketeer/auth/RequestMatcherHolder.java +++ b/src/main/java/com/tiketeer/Tiketeer/auth/RequestMatcherHolder.java @@ -27,6 +27,7 @@ public class RequestMatcherHolder { new RequestInfo(GET, "/members/*", RoleEnum.BUYER), new RequestInfo(GET, "/members", RoleEnum.BUYER), new RequestInfo(GET, "/members/*/purchases", RoleEnum.BUYER), + new RequestInfo(GET, "/members/*/purchases/*", RoleEnum.BUYER), new RequestInfo(GET, "/members/*/sale", RoleEnum.SELLER), new RequestInfo(POST, "/members/*/points", RoleEnum.BUYER), @@ -42,9 +43,13 @@ public class RequestMatcherHolder { new RequestInfo(PATCH, "/ticketings/*", RoleEnum.SELLER), new RequestInfo(DELETE, "/ticketings/*", RoleEnum.SELLER), + //ticket + new RequestInfo(POST, "/ticketings/*/tickets", RoleEnum.SELLER), + new RequestInfo(PUT, "/ticketings/*/tickets", RoleEnum.SELLER), + // purchase new RequestInfo(POST, "/purchases", RoleEnum.BUYER), - new RequestInfo(DELETE, "/purchases/*/tickets", RoleEnum.BUYER), + new RequestInfo(DELETE, "/purchases/*/items", RoleEnum.BUYER), // swagger new RequestInfo(GET, "/v3/api-docs/**", null), From 9a1fa5f7c2640b7c0a5592c350c3da32a223eb62 Mon Sep 17 00:00:00 2001 From: dla0510 Date: Fri, 21 Jun 2024 17:15:58 +0900 Subject: [PATCH 38/38] =?UTF-8?q?test=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/ticket/controller/TicketControllerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketControllerTest.java b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketControllerTest.java index fb917669..0c1a368a 100644 --- a/src/test/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketControllerTest.java +++ b/src/test/java/com/tiketeer/Tiketeer/domain/ticket/controller/TicketControllerTest.java @@ -95,7 +95,7 @@ void postTicketSuccess() throws Exception { // when mockMvc - .perform(post("/api/ticketing/{ticketingId}/tickets", ticketing.getId()) + .perform(post("/api/ticketings/{ticketingId}/tickets", ticketing.getId()) .contextPath("/api") .contentType(MediaType.APPLICATION_JSON) .characterEncoding("utf-8") @@ -143,7 +143,7 @@ void putTicketSuccess() throws Exception { // when mockMvc - .perform(put("/api/ticketing/{ticketingId}/tickets", ticketing.getId()) + .perform(put("/api/ticketings/{ticketingId}/tickets", ticketing.getId()) .contextPath("/api") .contentType(MediaType.APPLICATION_JSON) .characterEncoding("utf-8")