Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public ReservationController(ReservationService reservationService) {

@GetMapping("/reservation")
public String reservation() {
return "reservation";
return "new-reservation";
}

/*
Expand Down
63 changes: 63 additions & 0 deletions src/main/java/roomescape/controller/TimeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package roomescape.controller;

import jakarta.validation.Valid;
import java.net.URI;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
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;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import roomescape.dto.TimeCreateRequest;
import roomescape.dto.TimeCreateResponse;
import roomescape.model.Time;
import roomescape.service.TimeService;

@Controller
public class TimeController {
private final TimeService timeService;

public TimeController(TimeService timeService) {
this.timeService = timeService;
}

/*
* View Mapping
*/

@GetMapping("/time")
public String time() {
return "time";
}

/*
* API Mapping
*/

@ResponseBody
@GetMapping("/times")
public List<Time> getTimes() {
return timeService.getTimes();
}

@ResponseBody
@PostMapping("/times")
public ResponseEntity<TimeCreateResponse> createTime(@RequestBody @Valid TimeCreateRequest request) {
Time time = timeService.createTime(request);

URI location = URI.create("/times/" + time.id());
return ResponseEntity.created(location).body(TimeCreateResponse.from(time));
}

@ResponseBody
@ResponseStatus(HttpStatus.NO_CONTENT)
@DeleteMapping("/times/{id}")
public void deleteTime(@PathVariable int id) {
timeService.deleteTime(id);
}
}
7 changes: 2 additions & 5 deletions src/main/java/roomescape/dto/ReservationCreateRequest.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package roomescape.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalTime;
import jakarta.validation.constraints.Pattern;
import roomescape.validation.ValidDate;
import roomescape.validation.ValidTime;

public record ReservationCreateRequest(
@NotBlank(message = "이름은 공백일 수 없습니다.")
Expand All @@ -14,6 +11,6 @@ public record ReservationCreateRequest(
@ValidDate(message = "날짜 형식이 올바르지 않습니다. 예: yyyy-MM-dd")
String date,

@ValidTime(message = "시간 형식이 올바르지 않습니다. 예: HH:mm")
@Pattern(regexp = "^\\d+$", message = "시간 ID 형식이 올바르지 않습니다.")
String time
) { }
4 changes: 2 additions & 2 deletions src/main/java/roomescape/dto/ReservationCreateResponse.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package roomescape.dto;

import java.time.LocalDate;
import java.time.LocalTime;
import roomescape.model.Reservation;
import roomescape.model.Time;

public record ReservationCreateResponse(int id, String name, LocalDate date, LocalTime time) {
public record ReservationCreateResponse(int id, String name, LocalDate date, Time time) {
public static ReservationCreateResponse from(Reservation reservation) {
return new ReservationCreateResponse(reservation.id(), reservation.name(), reservation.date(), reservation.time());
}
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/roomescape/dto/TimeCreateRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package roomescape.dto;

import roomescape.validation.ValidTime;

public record TimeCreateRequest(
@ValidTime(message = "시간 형식이 올바르지 않습니다. 예: HH:mm")
String time
) { }
11 changes: 11 additions & 0 deletions src/main/java/roomescape/dto/TimeCreateResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package roomescape.dto;

import java.time.LocalTime;
import roomescape.model.Time;

public record TimeCreateResponse(int id, LocalTime time) {
public static TimeCreateResponse from(Time time) {
return new TimeCreateResponse(time.id(), time.value());
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package roomescape.exception;

public class DuplicateTimeException extends RuntimeException {
public DuplicateTimeException(String message) {
super(message);
}
}
32 changes: 31 additions & 1 deletion src/main/java/roomescape/exception/GlobalExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package roomescape.exception;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
Expand All @@ -9,20 +11,48 @@

@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

@ExceptionHandler(TimeNotFoundException.class)
public ResponseEntity<ErrorResponse> handleTimeNotFoundException(TimeNotFoundException e) {
log.error("TimeNotFoundException occurred:", e);

return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(ReservationNotFoundException.class)
public ResponseEntity<ErrorResponse> handleReservationNotFoundException(ReservationNotFoundException e) {
log.error("ReservationNotFoundException occurred:", e);

return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(DuplicateTimeException.class)
public ResponseEntity<ErrorResponse> handleDuplicateTimeException(DuplicateTimeException e) {
log.error("DuplicateTimeException occurred:", e);

return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(TimeInUseException.class)
public ResponseEntity<ErrorResponse> handleTimeInUseException(TimeInUseException e) {
log.error("TimeInUseException occurred:", e);

return new ResponseEntity<>(new ErrorResponse(e.getMessage()), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getAllErrors().stream().map((err) -> err.getDefaultMessage()).findFirst().orElse("입력 값 검증 실패");
log.error("MethodArgumentNotValidException occurred:", e);

return new ResponseEntity<>(new ErrorResponse(message), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleFallbackException() {
public ResponseEntity<ErrorResponse> handleFallbackException(Exception e) {
log.error("Exception occurred:", e);

return new ResponseEntity<>(new ErrorResponse("내부 서버 오류가 발생했습니다."), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
7 changes: 7 additions & 0 deletions src/main/java/roomescape/exception/TimeInUseException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package roomescape.exception;

public class TimeInUseException extends RuntimeException {
public TimeInUseException(String message) {
super(message);
}
}
7 changes: 7 additions & 0 deletions src/main/java/roomescape/exception/TimeNotFoundException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package roomescape.exception;

public class TimeNotFoundException extends RuntimeException {
public TimeNotFoundException(String message) {
super(message);
}
}
7 changes: 3 additions & 4 deletions src/main/java/roomescape/model/Reservation.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package roomescape.model;

import java.time.LocalDate;
import java.time.LocalTime;

public record Reservation(Integer id, String name, LocalDate date, LocalTime time) {
public static Reservation of(Integer id, String name, LocalDate date, LocalTime time) {
public record Reservation(Integer id, String name, LocalDate date, Time time) {
public static Reservation of(Integer id, String name, LocalDate date, Time time) {
return new Reservation(id, name, date, time);
}

public static Reservation create(String name, LocalDate date, LocalTime time) {
public static Reservation create(String name, LocalDate date, Time time) {
return new Reservation(null, name, date, time);
}
}
14 changes: 14 additions & 0 deletions src/main/java/roomescape/model/Time.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package roomescape.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.LocalTime;

public record Time(Integer id, @JsonProperty("time") LocalTime value) {
public static Time of(Integer id, LocalTime value) {
return new Time(id, value);
}

public static Time create(LocalTime value) {
return new Time(null, value);
}
}
13 changes: 8 additions & 5 deletions src/main/java/roomescape/repository/ReservationRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;
import roomescape.model.Reservation;
import roomescape.model.Time;

@Repository
public class ReservationRepository {
Expand All @@ -15,11 +16,13 @@ public class ReservationRepository {
private final JdbcTemplate jdbcTemplate;
private final SimpleJdbcInsert simpleJdbcInsert;
private final RowMapper<Reservation> reservationMapper = (rs, rowNum) -> {
Time time = Time.of(rs.getInt("time_id"), rs.getTime("time_value").toLocalTime());

return Reservation.of(
rs.getInt("id"),
rs.getString("name"),
rs.getDate("date").toLocalDate(),
rs.getTime("time").toLocalTime()
time
);
};

Expand All @@ -31,7 +34,7 @@ public ReservationRepository(JdbcTemplate jdbcTemplate) {
}

public List<Reservation> findAll() {
String query = "SELECT id, name, date, time FROM " + TABLE_NAME;
String query = "SELECT r.id AS reservation_id, r.name, r.date, t.id AS time_id, t.time AS time_value FROM " + TABLE_NAME + " AS r INNER JOIN time AS t ON r.time_id = t.id";

return jdbcTemplate.query(query, reservationMapper);
}
Expand All @@ -40,17 +43,17 @@ public Reservation save(Reservation reservation) {
Map<String, Object> params = Map.of(
"name", reservation.name(),
"date", reservation.date(),
"time", reservation.time()
"time_id", reservation.time().id()
);

Number id = simpleJdbcInsert.executeAndReturnKey(params);

return Reservation.of(id.intValue(), reservation.name(), reservation.date(), reservation.time());
}

public void deleteById(int id) {
public int deleteById(int id) {
String query = "DELETE FROM " + TABLE_NAME + " WHERE id = ?";

jdbcTemplate.update(query, id);
return jdbcTemplate.update(query, id);
}
}
60 changes: 60 additions & 0 deletions src/main/java/roomescape/repository/TimeRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package roomescape.repository;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;
import roomescape.model.Time;

@Repository
public class TimeRepository {
private static final String TABLE_NAME = "time";

private final JdbcTemplate jdbcTemplate;
private final SimpleJdbcInsert simpleJdbcInsert;
private final RowMapper<Time> timeMapper = (rs, rowNum) -> {
return Time.of(
rs.getInt("id"),
rs.getTime("time").toLocalTime()
);
};

public TimeRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate)
.withTableName(TABLE_NAME)
.usingGeneratedKeyColumns("id");
}

public List<Time> findAll() {
String query = "SELECT id, time FROM " + TABLE_NAME;

return jdbcTemplate.query(query, timeMapper);
}

public Optional<Time> findById(int id) {
String query = "SELECT id, time FROM " + TABLE_NAME + " WHERE id = ?";
List<Time> times = jdbcTemplate.query(query, timeMapper, id);

return times.isEmpty() ? Optional.empty() : Optional.of(times.get(0));
}

public Time save(Time time) {
Map<String, Object> params = Map.of(
"time", time.value()
);

Number id = simpleJdbcInsert.executeAndReturnKey(params);

return Time.of(id.intValue(), time.value());
}

public int deleteById(int id) {
String query = "DELETE FROM " + TABLE_NAME + " WHERE id = ?";

return jdbcTemplate.update(query, id);
}
}
Loading