diff --git a/src/main/java/com/first/flash/climbing/hold/application/HoldService.java b/src/main/java/com/first/flash/climbing/hold/application/HoldService.java new file mode 100644 index 00000000..32150b05 --- /dev/null +++ b/src/main/java/com/first/flash/climbing/hold/application/HoldService.java @@ -0,0 +1,50 @@ +package com.first.flash.climbing.hold.application; + +import com.first.flash.climbing.hold.application.dto.HoldCreateRequestDto; +import com.first.flash.climbing.hold.application.dto.HoldResponseDto; +import com.first.flash.climbing.hold.application.dto.HoldsResponseDto; +import com.first.flash.climbing.hold.domain.Hold; +import com.first.flash.climbing.hold.domain.HoldRepository; +import com.first.flash.climbing.hold.exception.exceptions.HoldNotFoundException; +import com.first.flash.climbing.sector.application.dto.SectorCreateRequestDto; +import com.first.flash.climbing.sector.application.dto.SectorDetailResponseDto; +import com.first.flash.climbing.sector.application.dto.SectorsDetailResponseDto; +import com.first.flash.climbing.sector.domain.Sector; +import com.first.flash.climbing.sector.domain.SectorInfo; +import com.first.flash.climbing.sector.exception.exceptions.SectorNotFoundException; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class HoldService { + + private final HoldRepository holdRepository; + + public Hold findById(final Long id) { + return holdRepository.findById(id) + .orElseThrow(() -> new HoldNotFoundException(id)); + } + + public HoldsResponseDto findAllHolds() { + List holdsResponse = holdRepository.findAll() + .stream() + .map(HoldResponseDto::toDto) + .toList(); + + return new HoldsResponseDto(holdsResponse); + } + + @Transactional + public HoldResponseDto saveHold(final HoldCreateRequestDto createRequestDto) { + Hold hold = createHold(createRequestDto); + return HoldResponseDto.toDto(holdRepository.save(hold)); + } + + private Hold createHold(final HoldCreateRequestDto createRequestDto) { + return Hold.of(createRequestDto.colorName(), createRequestDto.colorCode()); + } +} diff --git a/src/main/java/com/first/flash/climbing/hold/application/dto/HoldCreateRequestDto.java b/src/main/java/com/first/flash/climbing/hold/application/dto/HoldCreateRequestDto.java new file mode 100644 index 00000000..8eb98f1c --- /dev/null +++ b/src/main/java/com/first/flash/climbing/hold/application/dto/HoldCreateRequestDto.java @@ -0,0 +1,10 @@ +package com.first.flash.climbing.hold.application.dto; + +import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; + +public record HoldCreateRequestDto( + @NotNull(message = "홀드 색상 이름 정보는 비어있을 수 없습니다.") String colorName, + @NotNull(message = "홀드 색상 코드 정보는 비어있을 수 없습니다.") String colorCode) { + +} diff --git a/src/main/java/com/first/flash/climbing/hold/application/dto/HoldResponseDto.java b/src/main/java/com/first/flash/climbing/hold/application/dto/HoldResponseDto.java new file mode 100644 index 00000000..2b6c5732 --- /dev/null +++ b/src/main/java/com/first/flash/climbing/hold/application/dto/HoldResponseDto.java @@ -0,0 +1,10 @@ +package com.first.flash.climbing.hold.application.dto; + +import com.first.flash.climbing.hold.domain.Hold; + +public record HoldResponseDto(Long id, String colorName, String colorCode) { + + public static HoldResponseDto toDto(final Hold hold) { + return new HoldResponseDto(hold.getId(), hold.getColorName(), hold.getColorCode()); + } +} diff --git a/src/main/java/com/first/flash/climbing/hold/application/dto/HoldsResponseDto.java b/src/main/java/com/first/flash/climbing/hold/application/dto/HoldsResponseDto.java new file mode 100644 index 00000000..1f6d215f --- /dev/null +++ b/src/main/java/com/first/flash/climbing/hold/application/dto/HoldsResponseDto.java @@ -0,0 +1,7 @@ +package com.first.flash.climbing.hold.application.dto; + +import java.util.List; + +public record HoldsResponseDto(List holdList) { + +} diff --git a/src/main/java/com/first/flash/climbing/hold/domain/Hold.java b/src/main/java/com/first/flash/climbing/hold/domain/Hold.java new file mode 100644 index 00000000..0644f76c --- /dev/null +++ b/src/main/java/com/first/flash/climbing/hold/domain/Hold.java @@ -0,0 +1,31 @@ +package com.first.flash.climbing.hold.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Entity +@NoArgsConstructor +@Getter +@ToString +public class Hold { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String colorName; + private String colorCode; + + protected Hold(final String colorName, final String colorCode) { + this.colorName = colorName; + this.colorCode = colorCode; + } + + public static Hold of(final String colorName, final String colorCode) { + return new Hold(colorName, colorCode); + } +} diff --git a/src/main/java/com/first/flash/climbing/hold/domain/HoldRepository.java b/src/main/java/com/first/flash/climbing/hold/domain/HoldRepository.java new file mode 100644 index 00000000..2bb02724 --- /dev/null +++ b/src/main/java/com/first/flash/climbing/hold/domain/HoldRepository.java @@ -0,0 +1,14 @@ +package com.first.flash.climbing.hold.domain; + +import com.first.flash.climbing.sector.domain.Sector; +import java.util.List; +import java.util.Optional; + +public interface HoldRepository { + + Hold save(final Hold hold); + + Optional findById(final Long id); + + List findAll(); +} diff --git a/src/main/java/com/first/flash/climbing/hold/exception/HoldExceptionHandler.java b/src/main/java/com/first/flash/climbing/hold/exception/HoldExceptionHandler.java new file mode 100644 index 00000000..73054ba1 --- /dev/null +++ b/src/main/java/com/first/flash/climbing/hold/exception/HoldExceptionHandler.java @@ -0,0 +1,25 @@ +package com.first.flash.climbing.hold.exception; + +import com.first.flash.climbing.hold.exception.exceptions.HoldNotFoundException; +import com.first.flash.global.dto.ErrorResponseDto; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class HoldExceptionHandler { + + @ExceptionHandler(HoldNotFoundException.class) + public ResponseEntity handleHoldNotFoundException( + final HoldNotFoundException exception) { + return getResponseWithStatus(HttpStatus.NOT_FOUND, exception); + } + + private ResponseEntity getResponseWithStatus(final HttpStatus httpStatus, + final RuntimeException exception) { + ErrorResponseDto errorResponse = new ErrorResponseDto(exception.getMessage()); + return ResponseEntity.status(httpStatus) + .body(errorResponse); + } +} diff --git a/src/main/java/com/first/flash/climbing/hold/exception/exceptions/HoldNotFoundException.java b/src/main/java/com/first/flash/climbing/hold/exception/exceptions/HoldNotFoundException.java new file mode 100644 index 00000000..c5485321 --- /dev/null +++ b/src/main/java/com/first/flash/climbing/hold/exception/exceptions/HoldNotFoundException.java @@ -0,0 +1,15 @@ +package com.first.flash.climbing.hold.exception.exceptions; + +import com.first.flash.climbing.sector.exception.exceptions.SectorNotFoundException; +import com.first.flash.global.dto.ErrorResponseDto; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +public class HoldNotFoundException extends RuntimeException { + + public HoldNotFoundException(final Long id) { + super(String.format("아이디가 %s인 홀드를 찾을 수 없습니다.", id)); + } +} diff --git a/src/main/java/com/first/flash/climbing/hold/infrastructure/HoldJpaRepository.java b/src/main/java/com/first/flash/climbing/hold/infrastructure/HoldJpaRepository.java new file mode 100644 index 00000000..a78cedcb --- /dev/null +++ b/src/main/java/com/first/flash/climbing/hold/infrastructure/HoldJpaRepository.java @@ -0,0 +1,15 @@ +package com.first.flash.climbing.hold.infrastructure; + +import com.first.flash.climbing.hold.domain.Hold; +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface HoldJpaRepository extends JpaRepository { + + Hold save(final Hold hold); + + Optional findById(final Long id); + + List findAll(); +} diff --git a/src/main/java/com/first/flash/climbing/hold/infrastructure/HoldRepositoryImpl.java b/src/main/java/com/first/flash/climbing/hold/infrastructure/HoldRepositoryImpl.java new file mode 100644 index 00000000..177ca752 --- /dev/null +++ b/src/main/java/com/first/flash/climbing/hold/infrastructure/HoldRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.first.flash.climbing.hold.infrastructure; + +import com.first.flash.climbing.hold.domain.Hold; +import com.first.flash.climbing.hold.domain.HoldRepository; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class HoldRepositoryImpl implements HoldRepository { + + private final HoldJpaRepository holdJpaRepository; + + @Override + public Hold save(Hold hold) { + return holdJpaRepository.save(hold); + } + + @Override + public Optional findById(Long id) { + return holdJpaRepository.findById(id); + } + + @Override + public List findAll() { + return holdJpaRepository.findAll(); + } +} diff --git a/src/main/java/com/first/flash/climbing/hold/ui/HoldController.java b/src/main/java/com/first/flash/climbing/hold/ui/HoldController.java new file mode 100644 index 00000000..01d9a3d9 --- /dev/null +++ b/src/main/java/com/first/flash/climbing/hold/ui/HoldController.java @@ -0,0 +1,52 @@ +package com.first.flash.climbing.hold.ui; + +import com.first.flash.climbing.hold.application.HoldService; +import com.first.flash.climbing.hold.application.dto.HoldCreateRequestDto; +import com.first.flash.climbing.hold.application.dto.HoldResponseDto; +import com.first.flash.climbing.hold.application.dto.HoldsResponseDto; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +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.RestController; + +@Tag(name = "holds", description = "홀드 정보 관리 API") +@RestController +@RequestMapping +@RequiredArgsConstructor +public class HoldController { + + private final HoldService holdService; + + @Operation(summary = "모든 홀드 정보 조회", description = "모든 홀드 정보를 리스트로 반환") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "성공적으로 홀드를 조회", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = HoldsResponseDto.class))), + }) + @GetMapping("holds") + public ResponseEntity findAllHolds() { + return ResponseEntity.ok(holdService.findAllHolds()); + } + + @Operation(summary = "홀드 정보 생성", description = "홀드 정보를 생성") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "홀드 정보를 생성", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = HoldResponseDto.class))), + }) + @PostMapping("holds") + public ResponseEntity createHold( + @Valid @RequestBody final HoldCreateRequestDto createRequestDto) { + return ResponseEntity.status(HttpStatus.CREATED) + .body(holdService.saveHold(createRequestDto)); + } +} \ No newline at end of file