Skip to content

Commit

Permalink
[BSVR-100] 특정 경기장 내 블럭 & 블럭 내 열, 좌석 생성 API (#44)
Browse files Browse the repository at this point in the history
* feat: 코드 중복 custom exception 추가

* feat: 블럭 생성 controller 추가

* feat: 블럭, 열 생성 usecase, service 추가

* feat: 경기장 내 중복 코드의 블럭이 있는지 확인

* test: fake repository 수정

* feat: 블럭 생성 코드 추가

* feat: 블럭 생성 레포지토리 추가

* feat: 블럭 내 열 생성 구현

* feat: 열별 좌석 자동 채번 로직 추가

* feat: 좌석 생성 시그니처 변경

* feat: 좌석 일괄 등록 Jdbc 코드 추가

* feat: 블럭 열 일괄 등록 jdbc batch insert로 수정

* fix: GET -> POST로 변경

* fix: seat number 증가 코드 추가

* feat: row number 검증 코드 추가

* refactor: 불필요한 코드 삭제

* test: 좌석 자동 채번 테스트 코드 추가

* test: 블록 열 유효성 검증 테스트 추가

* fix: immutable list sort 이슈 해결
  • Loading branch information
EunjiShin authored Jul 18, 2024
1 parent 8e44974 commit 1ae7458
Show file tree
Hide file tree
Showing 30 changed files with 750 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.depromeet.spot.application.block;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;

import org.depromeet.spot.application.block.dto.request.CreateBlockRequest;
import org.depromeet.spot.usecase.port.in.block.CreateBlockUsecase;
import org.depromeet.spot.usecase.port.in.block.CreateBlockUsecase.CreateBlockCommand;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

@RestController
@Tag(name = "블록")
@RequiredArgsConstructor
@RequestMapping("/api/v1")
public class CreateBlockController {

private final CreateBlockUsecase createBlockUsecase;

@ResponseStatus(HttpStatus.OK)
@PostMapping("/stadiums/{stadiumId}/sections/{sectionId}/blocks")
@Operation(summary = "특정 야구 경기장 특정 구역 내의 신규 블럭(내부의 열, 좌석 포함)을 추가한다.")
public void create(
@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,
@RequestBody @Valid @NotNull CreateBlockRequest request) {
CreateBlockCommand command = request.toCommand();
createBlockUsecase.create(stadiumId, sectionId, command);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.depromeet.spot.application.block.dto.request;

import java.util.List;

import org.depromeet.spot.usecase.port.in.block.CreateBlockUsecase.CreateBlockCommand;
import org.depromeet.spot.usecase.port.in.block.CreateRowUsecase.CreateRowCommand;

public record CreateBlockRequest(String code, int maxRows, List<CreateRowRequest> rowInfos) {

public record CreateRowRequest(int number, int maxSeatNum) {
public CreateRowCommand toCommand() {
return CreateRowCommand.builder().number(number).maxSeatNum(maxSeatNum).build();
}
}

public CreateBlockCommand toCommand() {
List<CreateRowCommand> rowCommands =
rowInfos.stream().map(CreateRowRequest::toCommand).toList();
return new CreateBlockCommand(code, maxRows, rowCommands);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
@Getter
public enum BlockErrorCode implements ErrorCode {
BLOCK_NOT_FOUND(HttpStatus.NOT_FOUND, "BL001", "요청한 블록을 찾을 수 없습니다."),
BLOCK_CODE_DUPLICATE(HttpStatus.CONFLICT, "BL002", "경기장에 동일 코드의 블럭이 이미 존재합니다."),
INVALID_BLOCK_ROW(HttpStatus.BAD_REQUEST, "BL003", "블럭 열 정보가 잘못되었습니다."),
;

private final HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,20 @@ public BlockNotFoundException(Object obj) {
super(BlockErrorCode.BLOCK_NOT_FOUND.appended(obj));
}
}

public static class BlockCodeDuplicateException extends BlockException {
public BlockCodeDuplicateException() {
super(BlockErrorCode.BLOCK_CODE_DUPLICATE);
}

public BlockCodeDuplicateException(String str) {
super(BlockErrorCode.BLOCK_CODE_DUPLICATE.appended(str));
}
}

public static class InvalidBlockRowException extends BlockException {
public InvalidBlockRowException() {
super(BlockErrorCode.INVALID_BLOCK_ROW);
}
}
}
2 changes: 2 additions & 0 deletions domain/src/main/java/org/depromeet/spot/domain/seat/Seat.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import org.depromeet.spot.domain.stadium.Stadium;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
@AllArgsConstructor
public class Seat {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ public interface BlockJpaRepository extends JpaRepository<BlockEntity, Long> {
Optional<BlockEntity> findByStadiumIdAndCode(Long stadiumId, String code);

List<BlockEntity> findAllBySectionId(Long sectionId);

boolean existsByStadiumIdAndCode(Long stadiumId, String code);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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.jpa.block.repository.row.BlockRowJpaRepository;
import org.depromeet.spot.usecase.port.out.block.BlockRepository;
import org.springframework.stereotype.Repository;

Expand Down Expand Up @@ -61,6 +62,11 @@ public boolean existsById(final Long blockId) {
return blockJpaRepository.existsById(blockId);
}

@Override
public boolean existsByStadiumAndCode(Long stadiumId, String code) {
return blockJpaRepository.existsByStadiumIdAndCode(stadiumId, code);
}

@Override
public Block findById(final Long blockId) {
BlockEntity entity =
Expand All @@ -76,4 +82,10 @@ public Block findByStadiumAndCode(final Long stadiumId, final String code) {
.orElseThrow(BlockNotFoundException::new);
return entity.toDomain();
}

@Override
public Block save(Block block) {
BlockEntity entity = blockJpaRepository.save(BlockEntity.from(block));
return entity.toDomain();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.depromeet.spot.jpa.block.repository.row;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;

import org.depromeet.spot.domain.block.BlockRow;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import lombok.RequiredArgsConstructor;

@Repository
@RequiredArgsConstructor
public class BlockRowJdbcRepository {

private final JdbcTemplate jdbcTemplate;

public void createRows(List<BlockRow> rows) {
jdbcTemplate.batchUpdate(
"insert into block_rows" + "(block_id, number, max_seats) values (?, ?, ?)",
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setLong(1, rows.get(i).getBlock().getId());
ps.setInt(2, rows.get(i).getNumber());
ps.setInt(3, rows.get(i).getMaxSeats());
}

@Override
public int getBatchSize() {
return rows.size();
}
});
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.depromeet.spot.jpa.block.repository;
package org.depromeet.spot.jpa.block.repository.row;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.depromeet.spot.jpa.block.repository.row;

import java.util.List;

import org.depromeet.spot.domain.block.BlockRow;
import org.depromeet.spot.jpa.block.entity.BlockRowEntity;
import org.depromeet.spot.usecase.port.out.block.BlockRowRepository;
import org.springframework.stereotype.Repository;

import lombok.RequiredArgsConstructor;

@Repository
@RequiredArgsConstructor
public class BlockRowRepositoryImpl implements BlockRowRepository {

private final BlockRowJpaRepository blockRowJpaRepository;
private final BlockRowJdbcRepository blockRowJdbcRepository;

@Override
public void createAll(List<BlockRow> rows) {
blockRowJdbcRepository.createRows(rows);
}

@Override
public List<BlockRow> findAllByBlock(Long blockId) {
List<BlockRowEntity> entities =
blockRowJpaRepository.findAllByBlockIdOrderByNumberAsc(blockId);
return entities.stream().map(BlockRowEntity::toDomain).toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.depromeet.spot.jpa.seat.repository;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;

import org.depromeet.spot.domain.seat.Seat;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import lombok.RequiredArgsConstructor;

@Repository
@RequiredArgsConstructor
public class SeatJdbcRepository {

private final JdbcTemplate jdbcTemplate;

public void createSeats(List<Seat> seats) {
jdbcTemplate.batchUpdate(
"insert into seats"
+ "(stadium_id, section_id, block_id, row_id, seat_number) values (?, ?, ?, ?, ?)",
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setLong(1, seats.get(i).getStadium().getId());
ps.setLong(2, seats.get(i).getSection().getId());
ps.setLong(3, seats.get(i).getBlock().getId());
ps.setLong(4, seats.get(i).getRow().getId());
ps.setInt(5, seats.get(i).getSeatNumber());
}

@Override
public int getBatchSize() {
return seats.size();
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.depromeet.spot.jpa.seat.repository;

import java.util.List;

import org.depromeet.spot.domain.seat.Seat;
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 SeatJdbcRepository seatJdbcRepository;

@Override
public void saveAll(List<Seat> seats) {
seatJdbcRepository.createSeats(seats);
}
}
3 changes: 3 additions & 0 deletions usecase/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ dependencies {
// spring
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa:_") {
because("@Transactional을 위해 추가")
}
}

tasks.bootJar { enabled = false }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public interface BlockReadUsecase {

Block findByStadiumAndCode(Long stadiumId, String code);

void checkIsDuplicateCode(Long stadiumId, String code);

boolean existsById(Long blockId);

void checkExistsById(Long blockId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.depromeet.spot.usecase.port.in.block;

import java.util.List;

import org.depromeet.spot.usecase.port.in.block.CreateRowUsecase.CreateRowCommand;

public interface CreateBlockUsecase {

void create(Long stadiumId, Long sectionId, CreateBlockCommand command);

record CreateBlockCommand(String code, int maxRows, List<CreateRowCommand> rowInfos) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.depromeet.spot.usecase.port.in.block;

import java.util.List;

import org.depromeet.spot.domain.block.Block;

import lombok.Builder;

public interface CreateRowUsecase {

void createAll(Block block, List<CreateRowCommand> commands);

@Builder
record CreateRowCommand(int number, int maxSeatNum) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.depromeet.spot.usecase.port.in.block;

import java.util.List;

import org.depromeet.spot.domain.block.BlockRow;

public interface ReadBlockRowUsecase {

List<BlockRow> findAllByBlock(Long blockId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.depromeet.spot.usecase.port.in.seat;

public interface CreateSeatUsecase {

void createAllInBlock(Long blockId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ public interface BlockRepository {

boolean existsById(Long blockId);

boolean existsByStadiumAndCode(Long stadiumId, String code);

Block findById(Long blockId);

Block findByStadiumAndCode(Long stadiumId, String code);

Block save(Block block);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.depromeet.spot.usecase.port.out.block;

import java.util.List;

import org.depromeet.spot.domain.block.BlockRow;

public interface BlockRowRepository {

void createAll(List<BlockRow> rows);

List<BlockRow> findAllByBlock(Long blockId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.depromeet.spot.usecase.port.out.seat;

import java.util.List;

import org.depromeet.spot.domain.seat.Seat;

public interface SeatRepository {

void saveAll(List<Seat> seats);
}
Loading

0 comments on commit 1ae7458

Please sign in to comment.