diff --git a/application/src/main/java/org/depromeet/spot/application/block/BlockReadController.java b/application/src/main/java/org/depromeet/spot/application/block/BlockReadController.java index 6358e420..4b542e0e 100644 --- a/application/src/main/java/org/depromeet/spot/application/block/BlockReadController.java +++ b/application/src/main/java/org/depromeet/spot/application/block/BlockReadController.java @@ -6,8 +6,10 @@ import jakarta.validation.constraints.Positive; import org.depromeet.spot.application.block.dto.response.BlockCodeInfoResponse; +import org.depromeet.spot.application.block.dto.response.BlockInfoResponse; import org.depromeet.spot.usecase.port.in.block.BlockReadUsecase; import org.depromeet.spot.usecase.port.in.block.BlockReadUsecase.BlockCodeInfo; +import org.depromeet.spot.usecase.port.in.block.BlockReadUsecase.BlockInfo; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -45,4 +47,22 @@ public List findCodeInfosByStadium( List infos = blockReadUsecase.findCodeInfosByStadium(stadiumId, sectionId); return infos.stream().map(BlockCodeInfoResponse::from).toList(); } + + @ResponseStatus(HttpStatus.OK) + @GetMapping("stadiums/{stadiumId}/sections/{sectionId}/blocks/rows") + @Operation(summary = "특정 야구장 섹션 내에 있는 모든 블록 열/번 정보를 조회한다.") + public List findAllBlockInfoBy( + @PathVariable("stadiumId") + @NotNull + @Positive + @Parameter(name = "stadiumId", description = "야구 경기장 PK", required = true) + final Long stadiumId, + @PathVariable("sectionId") + @NotNull + @Positive + @Parameter(name = "sectionId", description = "구역 PK", required = true) + final Long sectionId) { + List infos = blockReadUsecase.findAllBlockInfoBy(stadiumId, sectionId); + return infos.stream().map(BlockInfoResponse::from).toList(); + } } diff --git a/application/src/main/java/org/depromeet/spot/application/block/dto/response/BlockInfoResponse.java b/application/src/main/java/org/depromeet/spot/application/block/dto/response/BlockInfoResponse.java new file mode 100644 index 00000000..2363c781 --- /dev/null +++ b/application/src/main/java/org/depromeet/spot/application/block/dto/response/BlockInfoResponse.java @@ -0,0 +1,15 @@ +package org.depromeet.spot.application.block.dto.response; + +import java.util.List; + +import org.depromeet.spot.usecase.port.in.block.BlockReadUsecase.BlockInfo; +import org.depromeet.spot.usecase.port.in.block.BlockReadUsecase.RowInfo; + +public record BlockInfoResponse(Long id, String code, List rowInfo) { + public static BlockInfoResponse from(BlockInfo blockInfo) { + List rowInfos = blockInfo.getRowInfo(); + List rowInfoResponses = + rowInfos.stream().map(RowInfoResponse::from).toList(); + return new BlockInfoResponse(blockInfo.getId(), blockInfo.getCode(), rowInfoResponses); + } +} diff --git a/application/src/main/java/org/depromeet/spot/application/block/dto/response/RowInfoResponse.java b/application/src/main/java/org/depromeet/spot/application/block/dto/response/RowInfoResponse.java new file mode 100644 index 00000000..1a600572 --- /dev/null +++ b/application/src/main/java/org/depromeet/spot/application/block/dto/response/RowInfoResponse.java @@ -0,0 +1,18 @@ +package org.depromeet.spot.application.block.dto.response; + +import org.depromeet.spot.usecase.port.in.block.BlockReadUsecase.RowInfo; + +import lombok.Builder; + +@Builder +public record RowInfoResponse(Long id, int number, int minSeatNum, int maxSeatNum) { + + public static RowInfoResponse from(RowInfo rowInfo) { + return RowInfoResponse.builder() + .id(rowInfo.getId()) + .number(rowInfo.getNumber()) + .minSeatNum(rowInfo.getMinSeatNum()) + .maxSeatNum(rowInfo.getMaxSeatNum()) + .build(); + } +} diff --git a/domain/src/main/java/org/depromeet/spot/domain/block/Block.java b/domain/src/main/java/org/depromeet/spot/domain/block/Block.java index 38b78fe4..f7150f78 100644 --- a/domain/src/main/java/org/depromeet/spot/domain/block/Block.java +++ b/domain/src/main/java/org/depromeet/spot/domain/block/Block.java @@ -11,6 +11,8 @@ public class Block { private final String code; private final Integer maxRows; + public static final int BLOCK_SEAT_START_NUM = 1; + public Block(Long id, Long stadiumId, Long sectionId, String code, Integer maxRows) { this.id = id; this.stadiumId = stadiumId; diff --git a/domain/src/main/java/org/depromeet/spot/domain/block/BlockRow.java b/domain/src/main/java/org/depromeet/spot/domain/block/BlockRow.java index 334574cd..dc3cb647 100644 --- a/domain/src/main/java/org/depromeet/spot/domain/block/BlockRow.java +++ b/domain/src/main/java/org/depromeet/spot/domain/block/BlockRow.java @@ -10,7 +10,7 @@ public class BlockRow { private final Long id; - private final Long blockId; + private final Block block; private final Integer number; private final Integer maxSeats; } diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/block/entity/BlockRowEntity.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/block/entity/BlockRowEntity.java index bb2524fb..8693f282 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/block/entity/BlockRowEntity.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/block/entity/BlockRowEntity.java @@ -1,7 +1,12 @@ package org.depromeet.spot.jpa.block.entity; import jakarta.persistence.Column; +import jakarta.persistence.ConstraintMode; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import org.depromeet.spot.domain.block.BlockRow; @@ -15,8 +20,12 @@ @NoArgsConstructor @AllArgsConstructor public class BlockRowEntity extends BaseEntity { - @Column(name = "block_id", nullable = false) - private Long blockId; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "block_id", + nullable = false, + foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)) + private BlockEntity block; @Column(name = "number", nullable = false) private Integer number; @@ -26,10 +35,12 @@ public class BlockRowEntity extends BaseEntity { public static BlockRowEntity from(BlockRow blockRow) { return new BlockRowEntity( - blockRow.getBlockId(), blockRow.getNumber(), blockRow.getMaxSeats()); + BlockEntity.from(blockRow.getBlock()), + blockRow.getNumber(), + blockRow.getMaxSeats()); } public BlockRow toDomain() { - return new BlockRow(this.getId(), blockId, number, maxSeats); + return new BlockRow(this.getId(), block.toDomain(), number, maxSeats); } } diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/seat/repository/SeatCustomRepository.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/block/repository/BlockCustomRepository.java similarity index 51% rename from infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/seat/repository/SeatCustomRepository.java rename to infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/block/repository/BlockCustomRepository.java index e59efc52..a87d6226 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/seat/repository/SeatCustomRepository.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/block/repository/BlockCustomRepository.java @@ -1,16 +1,15 @@ -package org.depromeet.spot.jpa.seat.repository; +package org.depromeet.spot.jpa.block.repository; import static com.querydsl.core.group.GroupBy.groupBy; import static com.querydsl.core.group.GroupBy.list; import static org.depromeet.spot.jpa.block.entity.QBlockEntity.blockEntity; import static org.depromeet.spot.jpa.block.entity.QBlockRowEntity.blockRowEntity; -import static org.depromeet.spot.jpa.seat.entity.QSeatEntity.seatEntity; import java.util.List; import java.util.Map; import org.depromeet.spot.jpa.block.entity.BlockEntity; -import org.depromeet.spot.jpa.seat.entity.SeatEntity; +import org.depromeet.spot.jpa.block.entity.BlockRowEntity; import org.springframework.stereotype.Repository; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -19,18 +18,16 @@ @Repository @RequiredArgsConstructor -public class SeatCustomRepository { +public class BlockCustomRepository { private final JPAQueryFactory queryFactory; - public Map> findBlockSeatsBy(final Long sectionId) { + public Map> findRowInfosBy(final Long sectionId) { return queryFactory - .from(seatEntity) + .from(blockRowEntity) .join(blockEntity) - .on(seatEntity.blockId.eq(blockEntity.id)) - .join(blockRowEntity) - .on(seatEntity.rowId.eq(blockRowEntity.id)) - .where(seatEntity.sectionId.eq(sectionId)) - .transform(groupBy(blockEntity).as(list(seatEntity))); + .on(blockRowEntity.block.id.eq(blockEntity.id)) + .where(blockEntity.sectionId.eq(sectionId)) + .transform(groupBy(blockEntity).as(list(blockRowEntity))); } } diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/block/repository/BlockRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/block/repository/BlockRepositoryImpl.java index 764abf62..3e55fa7b 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/block/repository/BlockRepositoryImpl.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/block/repository/BlockRepositoryImpl.java @@ -1,9 +1,13 @@ package org.depromeet.spot.jpa.block.repository; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import org.depromeet.spot.domain.block.Block; +import org.depromeet.spot.domain.block.BlockRow; import org.depromeet.spot.jpa.block.entity.BlockEntity; +import org.depromeet.spot.jpa.block.entity.BlockRowEntity; import org.depromeet.spot.usecase.port.out.block.BlockRepository; import org.springframework.stereotype.Repository; @@ -14,10 +18,25 @@ public class BlockRepositoryImpl implements BlockRepository { private final BlockJpaRepository blockJpaRepository; + private final BlockCustomRepository blockCustomRepository; @Override public List findAllBySection(final Long sectionId) { List entities = blockJpaRepository.findAllBySectionId(sectionId); return entities.stream().map(BlockEntity::toDomain).toList(); } + + @Override + public Map> findRowInfosBy(final Long sectionId) { + Map> entityMap = + blockCustomRepository.findRowInfosBy(sectionId); + return entityMap.entrySet().stream() + .collect( + Collectors.toMap( + entry -> entry.getKey().toDomain(), + entry -> + entry.getValue().stream() + .map(BlockRowEntity::toDomain) + .toList())); + } } diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/seat/repository/SeatRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/seat/repository/SeatRepositoryImpl.java deleted file mode 100644 index 034e26c6..00000000 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/seat/repository/SeatRepositoryImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.depromeet.spot.jpa.seat.repository; - -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import org.depromeet.spot.domain.block.Block; -import org.depromeet.spot.domain.seat.Seat; -import org.depromeet.spot.jpa.block.entity.BlockEntity; -import org.depromeet.spot.jpa.seat.entity.SeatEntity; -import org.depromeet.spot.usecase.port.out.seat.SeatRepository; -import org.springframework.stereotype.Repository; - -import lombok.RequiredArgsConstructor; - -@Repository -@RequiredArgsConstructor -public class SeatRepositoryImpl implements SeatRepository { - - private final SeatCustomRepository seatCustomRepository; - - @Override - public Map> findBlockSeatsBy(final Long sectionId) { - Map> entityMap = - seatCustomRepository.findBlockSeatsBy(sectionId); - return entityMap.entrySet().stream() - .collect( - Collectors.toMap( - entry -> entry.getKey().toDomain(), - entry -> - entry.getValue().stream() - .map(SeatEntity::toDomain) - .toList())); - } -} diff --git a/infrastructure/jpa/src/main/resources/application-jpa.yaml b/infrastructure/jpa/src/main/resources/application-jpa.yaml index c7affc08..c8f9190f 100644 --- a/infrastructure/jpa/src/main/resources/application-jpa.yaml +++ b/infrastructure/jpa/src/main/resources/application-jpa.yaml @@ -17,7 +17,7 @@ spring: sql: init: - mode: always # 필요한 경우 'never'로 변경 + mode: never # 필요한 경우 'never'로 변경 server: port: 8080 diff --git a/infrastructure/jpa/src/main/resources/data.sql b/infrastructure/jpa/src/main/resources/data.sql index 385293f3..e89273ec 100644 --- a/infrastructure/jpa/src/main/resources/data.sql +++ b/infrastructure/jpa/src/main/resources/data.sql @@ -23,6 +23,21 @@ VALUES (1, 1, '오렌지석', '응원석', 255, 255, 255), (2, 1, '네이비석', '프리미엄석', 100, 100, 100), (3, 1, '레드석', null, 100, 0, 0); +-- Block +INSERT INTO blocks (id, stadium_id, section_id, code, max_rows) +VALUES (1, 1, 1, "207", 3), + (2, 1, 1, "208", 2), + (3, 1, 1, "209", 1); + +-- Row +INSERT INTO block_rows (id, block_id, number, max_seats) +VALUES (1, 1, 1, 3), + (2, 1, 2, 5), + (3, 1, 3, 10), + (4, 2, 1, 13), + (5, 2, 2, 20), + (6, 3, 1, 15); + -- Reviews INSERT INTO reviews (id, user_id, stadium_id, block_id, row_id, seat_number, date_time, content, created_at, updated_at) VALUES diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/in/block/BlockReadUsecase.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/in/block/BlockReadUsecase.java index 1d439fac..b5565217 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/in/block/BlockReadUsecase.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/in/block/BlockReadUsecase.java @@ -3,16 +3,36 @@ import java.util.List; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; public interface BlockReadUsecase { List findCodeInfosByStadium(Long stadiumId, Long sectionId); + List findAllBlockInfoBy(final Long stadiumId, final Long sectionId); + @Getter @AllArgsConstructor class BlockCodeInfo { private final Long id; private final String code; } + + @Getter + @AllArgsConstructor + class BlockInfo { + private Long id; + private String code; + private List rowInfo; + } + + @Getter + @Builder + class RowInfo { + private Long id; + private int number; + private int minSeatNum; + private int maxSeatNum; + } } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/in/seat/ReadSeatUsecase.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/in/seat/ReadSeatUsecase.java deleted file mode 100644 index e28f6b40..00000000 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/in/seat/ReadSeatUsecase.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.depromeet.spot.usecase.port.in.seat; - -import java.util.List; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; - -public interface ReadSeatUsecase { - - List findAllBlockInfoBy(Long stadiumId, Long sectionId); - - @Getter - @AllArgsConstructor - class BlockInfo { - private Long id; - private String code; - private List rowInfo; - } - - @Getter - @Builder - class RowInfo { - private Long id; - private int number; - private int minSeatNum; - private int maxSeatNum; - } -} diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/block/BlockRepository.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/block/BlockRepository.java index b146f7d9..bc559cb8 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/block/BlockRepository.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/block/BlockRepository.java @@ -1,10 +1,14 @@ package org.depromeet.spot.usecase.port.out.block; import java.util.List; +import java.util.Map; import org.depromeet.spot.domain.block.Block; +import org.depromeet.spot.domain.block.BlockRow; public interface BlockRepository { List findAllBySection(Long sectionId); + + Map> findRowInfosBy(Long sectionId); } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/seat/SeatRepository.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/seat/SeatRepository.java deleted file mode 100644 index 17b2d412..00000000 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/seat/SeatRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.depromeet.spot.usecase.port.out.seat; - -import java.util.List; -import java.util.Map; - -import org.depromeet.spot.domain.block.Block; -import org.depromeet.spot.domain.seat.Seat; - -public interface SeatRepository { - - Map> findBlockSeatsBy(Long sectionId); -} diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/block/BlockReadService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/block/BlockReadService.java index 4e0336cc..150e67b8 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/block/BlockReadService.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/block/BlockReadService.java @@ -1,10 +1,17 @@ package org.depromeet.spot.usecase.service.block; +import static org.depromeet.spot.domain.block.Block.BLOCK_SEAT_START_NUM; + +import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import org.depromeet.spot.common.exception.section.SectionException.SectionNotBelongStadiumException; import org.depromeet.spot.common.exception.stadium.StadiumException.StadiumNotFoundException; import org.depromeet.spot.domain.block.Block; +import org.depromeet.spot.domain.block.BlockRow; import org.depromeet.spot.usecase.port.in.block.BlockReadUsecase; import org.depromeet.spot.usecase.port.in.section.SectionReadUsecase; import org.depromeet.spot.usecase.port.in.stadium.StadiumReadUsecase; @@ -32,4 +39,47 @@ public List findCodeInfosByStadium(final Long stadiumId, final Lo List blocks = blockRepository.findAllBySection(sectionId); return blocks.stream().map(b -> new BlockCodeInfo(b.getId(), b.getCode())).toList(); } + + @Override + public List findAllBlockInfoBy(final Long stadiumId, final Long sectionId) { + List result = new ArrayList<>(); + if (!stadiumReadUsecase.existsById(sectionId)) { + throw new StadiumNotFoundException(); + } + + if (!sectionReadUsecase.existsInStadium(stadiumId, sectionId)) { + throw new SectionNotBelongStadiumException(); + } + + Map> blockRows = blockRepository.findRowInfosBy(sectionId); + for (Entry> entry : blockRows.entrySet()) { + Block block = entry.getKey(); + List rowInfos = getBlockRowInfos(entry.getValue()); + result.add(new BlockInfo(block.getId(), block.getCode(), rowInfos)); + } + + result.sort(Comparator.comparing(BlockInfo::getId)); + result.forEach(block -> block.getRowInfo().sort(Comparator.comparing(RowInfo::getNumber))); + + return result; + } + + public List getBlockRowInfos(List seats) { + List rowInfos = new ArrayList<>(); + int lastSeatNum = BLOCK_SEAT_START_NUM; + + for (BlockRow row : seats) { + int minSeatNum = lastSeatNum; + int maxSeatNum = row.getMaxSeats(); + rowInfos.add( + RowInfo.builder() + .id(row.getId()) + .number(row.getNumber()) + .maxSeatNum(maxSeatNum) + .minSeatNum(minSeatNum) + .build()); + lastSeatNum = ++maxSeatNum; + } + return rowInfos; + } } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/seat/ReadSeatService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/seat/ReadSeatService.java deleted file mode 100644 index e09bf477..00000000 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/seat/ReadSeatService.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.depromeet.spot.usecase.service.seat; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.stream.Collectors; - -import org.depromeet.spot.common.exception.section.SectionException.SectionNotBelongStadiumException; -import org.depromeet.spot.common.exception.stadium.StadiumException.StadiumNotFoundException; -import org.depromeet.spot.domain.block.Block; -import org.depromeet.spot.domain.block.BlockRow; -import org.depromeet.spot.domain.seat.Seat; -import org.depromeet.spot.usecase.port.in.seat.ReadSeatUsecase; -import org.depromeet.spot.usecase.port.in.section.SectionReadUsecase; -import org.depromeet.spot.usecase.port.in.stadium.StadiumReadUsecase; -import org.depromeet.spot.usecase.port.out.seat.SeatRepository; -import org.springframework.stereotype.Service; - -import lombok.RequiredArgsConstructor; - -@Service -@RequiredArgsConstructor -public class ReadSeatService implements ReadSeatUsecase { - - private final SeatRepository seatRepository; - private final StadiumReadUsecase stadiumReadUsecase; - private final SectionReadUsecase sectionReadUsecase; - - @Override - public List findAllBlockInfoBy(final Long stadiumId, final Long sectionId) { - List result = new ArrayList<>(); - if (!stadiumReadUsecase.existsById(sectionId)) { - throw new StadiumNotFoundException(); - } - - if (!sectionReadUsecase.existsInStadium(stadiumId, sectionId)) { - throw new SectionNotBelongStadiumException(); - } - - Map> seatPerBlock = seatRepository.findBlockSeatsBy(sectionId); - for (Entry> entry : seatPerBlock.entrySet()) { - Block block = entry.getKey(); - List rowInfos = getBlockRowInfos(entry.getValue()); - result.add(new BlockInfo(block.getId(), block.getCode(), rowInfos)); - } - - return result; - } - - public List getBlockRowInfos(List seats) { - List rowInfos = new ArrayList<>(); - int lastSeatNum = 1; - - Map> seatsByRow = - seats.stream().collect(Collectors.groupingBy(Seat::getRow)); - for (Entry> seatEntry : seatsByRow.entrySet()) { - BlockRow row = seatEntry.getKey(); - int minSeatNum = lastSeatNum; - int maxSeatNum = seatEntry.getKey().getMaxSeats(); - lastSeatNum = maxSeatNum; - rowInfos.add( - RowInfo.builder() - .id(row.getId()) - .number(row.getNumber()) - .maxSeatNum(maxSeatNum) - .minSeatNum(minSeatNum) - .build()); - } - return rowInfos; - } -}