diff --git a/build.gradle b/build.gradle index d5f9249b..fcf72cb6 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' diff --git a/src/main/java/roomescape/authentication/MemberAuthInfo.java b/src/main/java/roomescape/authentication/MemberAuthInfo.java index d2d2027f..c5dbaec7 100644 --- a/src/main/java/roomescape/authentication/MemberAuthInfo.java +++ b/src/main/java/roomescape/authentication/MemberAuthInfo.java @@ -1,6 +1,7 @@ package roomescape.authentication; public record MemberAuthInfo( + Long id, String name, String role) { } diff --git a/src/main/java/roomescape/authentication/jwt/JwtAuthenticationInfoExtractor.java b/src/main/java/roomescape/authentication/jwt/JwtAuthenticationInfoExtractor.java index a64bc8d2..93b915ce 100644 --- a/src/main/java/roomescape/authentication/jwt/JwtAuthenticationInfoExtractor.java +++ b/src/main/java/roomescape/authentication/jwt/JwtAuthenticationInfoExtractor.java @@ -33,10 +33,11 @@ public MemberAuthInfo extractMemberAuthInfoFromToken(String token) { .parseClaimsJws(token) .getBody(); + Long id = Long.valueOf(claims.getSubject()); String name = claims.get("name", String.class); String role = claims.get("role", String.class); - return new MemberAuthInfo(name, role); + return new MemberAuthInfo(id, name, role); } catch (JwtException e) { throw new JwtValidationException("유효하지 않은 JWT 토큰입니다.", e); } diff --git a/src/main/java/roomescape/exception/DuplicateReservationException.java b/src/main/java/roomescape/exception/DuplicateReservationException.java new file mode 100644 index 00000000..255d8b28 --- /dev/null +++ b/src/main/java/roomescape/exception/DuplicateReservationException.java @@ -0,0 +1,7 @@ +package roomescape.exception; + +public class DuplicateReservationException extends RuntimeException { + public DuplicateReservationException(String message) { + super(message); + } +} diff --git a/src/main/java/roomescape/exception/GeneralExceptionHandler.java b/src/main/java/roomescape/exception/GeneralExceptionHandler.java index d14e8f5f..6540e33f 100644 --- a/src/main/java/roomescape/exception/GeneralExceptionHandler.java +++ b/src/main/java/roomescape/exception/GeneralExceptionHandler.java @@ -1,31 +1,33 @@ package roomescape.exception; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +@Slf4j @RestControllerAdvice public class GeneralExceptionHandler { - @ExceptionHandler(MemberNotFoundException.class) - public ResponseEntity handleMemberNotFound(MemberNotFoundException e) { + @ExceptionHandler({MemberNotFoundException.class, JwtValidationException.class, JwtProviderException.class}) + public ResponseEntity handleMemberNotFound(Exception e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); } - @ExceptionHandler(JwtValidationException.class) - public ResponseEntity handleJwtValidationException(JwtValidationException e) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); + @ExceptionHandler({TimeNotFoundException.class, ThemeNotFoundException.class}) + public ResponseEntity handleTimeNotFound(Exception e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); } - @ExceptionHandler(JwtProviderException.class) - public ResponseEntity handleJwtProviderException(JwtProviderException e) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); + @ExceptionHandler(DuplicateReservationException.class) + public ResponseEntity handleDuplicatedReservation(DuplicateReservationException e) { + return ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage()); } @ExceptionHandler(Exception.class) public ResponseEntity handleGeneralException(Exception e) { - e.printStackTrace(); + log.error("Exception [Err_Location] : {}", e.getStackTrace()[0]); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); } } diff --git a/src/main/java/roomescape/exception/MemberNotFoundException.java b/src/main/java/roomescape/exception/MemberNotFoundException.java index b8a7db7a..2df644f2 100644 --- a/src/main/java/roomescape/exception/MemberNotFoundException.java +++ b/src/main/java/roomescape/exception/MemberNotFoundException.java @@ -1,7 +1,8 @@ package roomescape.exception; public class MemberNotFoundException extends RuntimeException { - public MemberNotFoundException(String message, Throwable cause) { - super(message, cause); + + public MemberNotFoundException(String message) { + super(message); } } diff --git a/src/main/java/roomescape/exception/ThemeNotFoundException.java b/src/main/java/roomescape/exception/ThemeNotFoundException.java new file mode 100644 index 00000000..a0729e1a --- /dev/null +++ b/src/main/java/roomescape/exception/ThemeNotFoundException.java @@ -0,0 +1,7 @@ +package roomescape.exception; + +public class ThemeNotFoundException extends RuntimeException { + public ThemeNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/roomescape/exception/TimeNotFoundException.java b/src/main/java/roomescape/exception/TimeNotFoundException.java new file mode 100644 index 00000000..6d6bfacb --- /dev/null +++ b/src/main/java/roomescape/exception/TimeNotFoundException.java @@ -0,0 +1,7 @@ +package roomescape.exception; + +public class TimeNotFoundException extends RuntimeException { + public TimeNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/roomescape/login/LoginService.java b/src/main/java/roomescape/login/LoginService.java index f6c41bf6..2c617587 100644 --- a/src/main/java/roomescape/login/LoginService.java +++ b/src/main/java/roomescape/login/LoginService.java @@ -1,38 +1,36 @@ package roomescape.login; import lombok.RequiredArgsConstructor; -import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.stereotype.Service; import roomescape.authentication.MemberAuthInfo; import roomescape.authentication.AuthenticationResponse; import roomescape.authentication.AuthenticationService; import roomescape.member.Member; -import roomescape.member.MemberDao; + import roomescape.exception.MemberNotFoundException; +import roomescape.member.MemberRepository; @Service @RequiredArgsConstructor public class LoginService { - private final MemberDao memberDao; + + private final MemberRepository memberRepository; private final AuthenticationService authenticationService; + public AuthenticationResponse login(LoginRequest loginRequest) { - try { - Member member = memberDao.findByEmailAndPassword(loginRequest.email(), loginRequest.password()); + Member member = memberRepository.findByEmailAndPassword(loginRequest.email(), loginRequest.password()) + .orElseThrow(() -> new MemberNotFoundException("입력한 이메일 혹은 비밀번호로 가입한 회원을 찾을 수 없습니다.")); + return authenticationService.createToken(member); - } catch (EmptyResultDataAccessException e) { - throw new MemberNotFoundException("이메일 혹은 비밀번호가 맞지 않습니다.", e); - } } public LoginCheckResponse checkLogin(MemberAuthInfo memberAuthInfo) { - try { - Member member = memberDao.findByName(memberAuthInfo.name()); - return new LoginCheckResponse(member.getName()); - } catch (EmptyResultDataAccessException e) { - throw new MemberNotFoundException("로그인이 되지 않은 상태입니다.", e); - } + Member member= memberRepository.findByName(memberAuthInfo.name()) + .orElseThrow(() -> new MemberNotFoundException("로그인 된 회원이 아닙니다.")); + + return new LoginCheckResponse(member.getName()); } } diff --git a/src/main/java/roomescape/member/Member.java b/src/main/java/roomescape/member/Member.java index 903aaa9b..0cfae7ef 100644 --- a/src/main/java/roomescape/member/Member.java +++ b/src/main/java/roomescape/member/Member.java @@ -1,10 +1,26 @@ package roomescape.member; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Member { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private String name; + private String email; + private String password; + private String role; public Member(Long id, String name, String email, String role) { diff --git a/src/main/java/roomescape/member/MemberDao.java b/src/main/java/roomescape/member/MemberDao.java deleted file mode 100644 index 81f77f4c..00000000 --- a/src/main/java/roomescape/member/MemberDao.java +++ /dev/null @@ -1,55 +0,0 @@ -package roomescape.member; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; - -@Repository -public class MemberDao { - private JdbcTemplate jdbcTemplate; - - public MemberDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public Member save(Member member) { - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - var ps = connection.prepareStatement("INSERT INTO member(name, email, password, role) VALUES (?, ?, ?, ?)", new String[]{"id"}); - ps.setString(1, member.getName()); - ps.setString(2, member.getEmail()); - ps.setString(3, member.getPassword()); - ps.setString(4, member.getRole()); - return ps; - }, keyHolder); - - return new Member(keyHolder.getKey().longValue(), member.getName(), member.getEmail(), "USER"); - } - - public Member findByEmailAndPassword(String email, String password) { - return jdbcTemplate.queryForObject( - "SELECT id, name, email, role FROM member WHERE email = ? AND password = ?", - (rs, rowNum) -> new Member( - rs.getLong("id"), - rs.getString("name"), - rs.getString("email"), - rs.getString("role") - ), - email, password - ); - } - - public Member findByName(String name) { - return jdbcTemplate.queryForObject( - "SELECT id, name, email, role FROM member WHERE name = ?", - (rs, rowNum) -> new Member( - rs.getLong("id"), - rs.getString("name"), - rs.getString("email"), - rs.getString("role") - ), - name - ); - } -} diff --git a/src/main/java/roomescape/member/MemberRepository.java b/src/main/java/roomescape/member/MemberRepository.java new file mode 100644 index 00000000..c0ee791c --- /dev/null +++ b/src/main/java/roomescape/member/MemberRepository.java @@ -0,0 +1,12 @@ +package roomescape.member; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface MemberRepository extends JpaRepository { + + Optional findByEmailAndPassword(String email, String password); + + Optional findByName(String name); +} diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index bec76725..47b4c2b5 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -1,17 +1,16 @@ package roomescape.member; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @Service +@RequiredArgsConstructor public class MemberService { - private MemberDao memberDao; - public MemberService(MemberDao memberDao) { - this.memberDao = memberDao; - } + private final MemberRepository memberRepository; public MemberResponse createMember(MemberRequest memberRequest) { - Member member = memberDao.save(new Member(memberRequest.name(), memberRequest.email(), memberRequest.password(), "USER")); + Member member = memberRepository.save(new Member(memberRequest.name(), memberRequest.email(), memberRequest.password(), "USER")); return new MemberResponse(member.getId(), member.getName(), member.getEmail()); } } diff --git a/src/main/java/roomescape/reservation/MyReservationResponse.java b/src/main/java/roomescape/reservation/MyReservationResponse.java new file mode 100644 index 00000000..c7edc9c9 --- /dev/null +++ b/src/main/java/roomescape/reservation/MyReservationResponse.java @@ -0,0 +1,10 @@ +package roomescape.reservation; + +public record MyReservationResponse( + Long reservationId, + String theme, + String date, + String time, + String status +) { +} diff --git a/src/main/java/roomescape/reservation/Reservation.java b/src/main/java/roomescape/reservation/Reservation.java index 83a7edf1..11e2c76f 100644 --- a/src/main/java/roomescape/reservation/Reservation.java +++ b/src/main/java/roomescape/reservation/Reservation.java @@ -1,23 +1,47 @@ package roomescape.reservation; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; +import roomescape.member.Member; import roomescape.theme.Theme; import roomescape.time.Time; +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Reservation { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private String name; + private String date; + + @ManyToOne + @JoinColumn(name = "time_id") + @OnDelete(action = OnDeleteAction.CASCADE) private Time time; + + @ManyToOne + @JoinColumn(name = "theme_id") + @OnDelete(action = OnDeleteAction.CASCADE) private Theme theme; - public Reservation(Long id, String name, String date, Time time, Theme theme) { - this.id = id; + @ManyToOne + @JoinColumn(name = "member_id") + private Member member; + + public Reservation(Member member, String name, String date, Time time, Theme theme) { + this.member = member; this.name = name; this.date = date; this.time = time; this.theme = theme; } - public Reservation(String name, String date, Time time, Theme theme) { this.name = name; this.date = date; @@ -25,10 +49,6 @@ public Reservation(String name, String date, Time time, Theme theme) { this.theme = theme; } - public Reservation() { - - } - public Long getId() { return id; } @@ -48,4 +68,8 @@ public Time getTime() { public Theme getTheme() { return theme; } + + public Member getMember() { + return member; + } } diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index 0ef4bae8..902da5ec 100644 --- a/src/main/java/roomescape/reservation/ReservationController.java +++ b/src/main/java/roomescape/reservation/ReservationController.java @@ -1,5 +1,6 @@ package roomescape.reservation; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -13,14 +14,11 @@ import java.util.List; @RestController +@RequiredArgsConstructor public class ReservationController { private final ReservationService reservationService; - public ReservationController(ReservationService reservationService) { - this.reservationService = reservationService; - } - @GetMapping("/reservations") public List list() { return reservationService.findAll(); @@ -29,19 +27,23 @@ public List list() { @PostMapping("/reservations") public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, MemberAuthInfo memberAuthInfo) { if ( memberAuthInfo == null - || reservationRequest.getDate() == null - || reservationRequest.getTheme() == null - || reservationRequest.getTime() == null) { + || reservationRequest.date() == null + || reservationRequest.theme() == null + || reservationRequest.time() == null) { return ResponseEntity.badRequest().build(); } - if (reservationRequest.getName() == null) { - reservationRequest.setName(memberAuthInfo.name()); - } + ReservationResponse reservation = reservationService.save(reservationRequest, memberAuthInfo); + + return ResponseEntity.created(URI.create("/reservations/" + reservation.id())).body(reservation); + } + + @GetMapping("/reservations-mine") + public List myReservationLists(MemberAuthInfo memberAuthInfo){ - ReservationResponse reservation = reservationService.save(reservationRequest); + List myReservations = reservationService.findMyReservations(memberAuthInfo); - return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation); + return myReservations; } @DeleteMapping("/reservations/{id}") diff --git a/src/main/java/roomescape/reservation/ReservationDao.java b/src/main/java/roomescape/reservation/ReservationDao.java deleted file mode 100644 index a4972430..00000000 --- a/src/main/java/roomescape/reservation/ReservationDao.java +++ /dev/null @@ -1,127 +0,0 @@ -package roomescape.reservation; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; -import roomescape.theme.Theme; -import roomescape.time.Time; - -import java.sql.PreparedStatement; -import java.util.List; - -@Repository -public class ReservationDao { - - private final JdbcTemplate jdbcTemplate; - - public ReservationDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public List findAll() { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id", - - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } - - public Reservation save(ReservationRequest reservationRequest) { - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - PreparedStatement ps = connection.prepareStatement("INSERT INTO reservation(date, name, theme_id, time_id) VALUES (?, ?, ?, ?)", new String[]{"id"}); - ps.setString(1, reservationRequest.getDate()); - ps.setString(2, reservationRequest.getName()); - ps.setLong(3, reservationRequest.getTheme()); - ps.setLong(4, reservationRequest.getTime()); - return ps; - }, keyHolder); - - Time time = jdbcTemplate.queryForObject("SELECT * FROM time WHERE id = ?", - (rs, rowNum) -> new Time(rs.getLong("id"), rs.getString("time_value")), - reservationRequest.getTime()); - - Theme theme = jdbcTemplate.queryForObject("SELECT * FROM theme WHERE id = ?", - (rs, rowNum) -> new Theme(rs.getLong("id"), rs.getString("name"), rs.getString("description")), - reservationRequest.getTheme()); - - return new Reservation( - keyHolder.getKey().longValue(), - reservationRequest.getName(), - reservationRequest.getDate(), - time, - theme - ); - } - - public void deleteById(Long id) { - jdbcTemplate.update("DELETE FROM reservation WHERE id = ?", id); - } - - public List findReservationsByDateAndTheme(String date, Long themeId) { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id" + - "WHERE r.date = ? AND r.theme_id = ?", - new Object[]{date, themeId}, - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } - - public List findByDateAndThemeId(String date, Long themeId) { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id " + - "WHERE r.date = ? AND r.theme_id = ?", - new Object[]{date, themeId}, - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } -} diff --git a/src/main/java/roomescape/reservation/ReservationRepository.java b/src/main/java/roomescape/reservation/ReservationRepository.java new file mode 100644 index 00000000..f0402302 --- /dev/null +++ b/src/main/java/roomescape/reservation/ReservationRepository.java @@ -0,0 +1,14 @@ +package roomescape.reservation; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface ReservationRepository extends JpaRepository { + + List findByDateAndThemeId(String date, Long themeId); + + List findByName(String name); + + boolean existsByDateAndTimeIdAndThemeId(String date, Long timeId, Long themeId); +} diff --git a/src/main/java/roomescape/reservation/ReservationRequest.java b/src/main/java/roomescape/reservation/ReservationRequest.java index a07a8483..7143bc92 100644 --- a/src/main/java/roomescape/reservation/ReservationRequest.java +++ b/src/main/java/roomescape/reservation/ReservationRequest.java @@ -1,28 +1,9 @@ package roomescape.reservation; -public class ReservationRequest { - private String name; - private String date; - private Long theme; - private Long time; - - public String getName() { - return name; - } - - public String getDate() { - return date; - } - - public Long getTheme() { - return theme; - } - - public Long getTime() { - return time; - } - - public void setName(String name) { - this.name = name; - } +public record ReservationRequest( + String name, + String date, + Long theme, + Long time){ } + diff --git a/src/main/java/roomescape/reservation/ReservationResponse.java b/src/main/java/roomescape/reservation/ReservationResponse.java index 41360a36..4e97aaa6 100644 --- a/src/main/java/roomescape/reservation/ReservationResponse.java +++ b/src/main/java/roomescape/reservation/ReservationResponse.java @@ -1,37 +1,10 @@ package roomescape.reservation; -public class ReservationResponse { - private Long id; - private String name; - private String theme; - private String date; - private String time; - - public ReservationResponse(Long id, String name, String theme, String date, String time) { - this.id = id; - this.name = name; - this.theme = theme; - this.date = date; - this.time = time; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getTheme() { - return theme; - } - - public String getDate() { - return date; - } - - public String getTime() { - return time; - } +public record ReservationResponse( + Long id, + String name, + String theme, + String date, + String time +) { } diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java index bd331332..3ce2ad2f 100644 --- a/src/main/java/roomescape/reservation/ReservationService.java +++ b/src/main/java/roomescape/reservation/ReservationService.java @@ -1,30 +1,124 @@ package roomescape.reservation; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import roomescape.authentication.MemberAuthInfo; +import roomescape.exception.DuplicateReservationException; +import roomescape.exception.MemberNotFoundException; +import roomescape.exception.ThemeNotFoundException; +import roomescape.exception.TimeNotFoundException; +import roomescape.member.Member; +import roomescape.member.MemberRepository; +import roomescape.theme.Theme; +import roomescape.theme.ThemeRepository; +import roomescape.time.Time; +import roomescape.time.TimeRepository; +import roomescape.waiting.WaitingRepository; +import roomescape.waiting.WaitingWithRank; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; @Service +@RequiredArgsConstructor public class ReservationService { - private ReservationDao reservationDao; - public ReservationService(ReservationDao reservationDao) { - this.reservationDao = reservationDao; + private final ReservationRepository reservationRepository; + private final ThemeRepository themeRepository; + private final TimeRepository timeRepository; + private final MemberRepository memberRepository; + private final WaitingRepository waitingRepository; + + public ReservationResponse save(ReservationRequest reservationRequest, MemberAuthInfo memberAuthInfo) { + + if (reservationRequest.name() == null) { + reservationRequest = new ReservationRequest( + memberAuthInfo.name(), + reservationRequest.date(), + reservationRequest.theme(), + reservationRequest.time()); + } + + Theme theme = themeRepository.findById(reservationRequest.theme()) + .orElseThrow(() -> new ThemeNotFoundException("해당 테마를 찾을 수 없습니다.")); + Time time = timeRepository.findById(reservationRequest.time()) + .orElseThrow(() -> new TimeNotFoundException("예약 시간을 찾을 수 없습니다.")); + + Member member = null; + if (memberAuthInfo.id() != null) { //관리자가 아닌, 사용자일 경우 + member = memberRepository.findById(memberAuthInfo.id()) + .orElseThrow(() -> new MemberNotFoundException("가입된 회원이 아닙니다.")); + } + + validateDuplicateReservation(reservationRequest); + + Reservation reservation = new Reservation ( + member, + reservationRequest.name(), + reservationRequest.date(), + time, + theme + ); + + reservationRepository.save(reservation); + + return new ReservationResponse(reservation.getId(), reservationRequest.name(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); + } + + public List findAll() { + return reservationRepository.findAll().stream() + .map(it -> new ReservationResponse(it.getId(), it.getName(), it.getTheme().getName(), it.getDate(), it.getTime().getValue())) + .toList(); } - public ReservationResponse save(ReservationRequest reservationRequest) { - Reservation reservation = reservationDao.save(reservationRequest); + public List findMyReservations(MemberAuthInfo memberAuthInfo) { + + List reservationResponses = findAllByMemberName(memberAuthInfo.name()); + List myReservationResponses1 = reservationResponses + .stream() + .map((it -> new MyReservationResponse( + it.id(), + it.theme(), + it.date(), + it.time(), + "예약"))) + .collect(Collectors.toList()); + + List waitingWithRanks = waitingRepository.findWaitingsWithRankByMemberId(memberAuthInfo.id()); + List myReservationResponses2 = waitingWithRanks.stream() + .map((it -> new MyReservationResponse( + it.getWaiting().getId(), + it.getWaiting().getTheme().getName(), + it.getWaiting().getDate(), + it.getWaiting().getTime().getValue(), + it.getRank()+ 1 + "번째 예약대기"))) + .collect(Collectors.toList()); - return new ReservationResponse(reservation.getId(), reservationRequest.getName(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); + return Stream.concat(myReservationResponses1.stream(), myReservationResponses2.stream()) + .collect(Collectors.toList()); } public void deleteById(Long id) { - reservationDao.deleteById(id); + reservationRepository.deleteById(id); } - public List findAll() { - return reservationDao.findAll().stream() + private List findAllByMemberName(String name) { + return reservationRepository.findByName(name).stream() .map(it -> new ReservationResponse(it.getId(), it.getName(), it.getTheme().getName(), it.getDate(), it.getTime().getValue())) .toList(); + + } + + private void validateDuplicateReservation(ReservationRequest reservationRequest) { + boolean exists = reservationRepository.existsByDateAndTimeIdAndThemeId( + reservationRequest.date(), + reservationRequest.time(), + reservationRequest.theme() + ); + + if (exists) { + throw new DuplicateReservationException("이미 예약이 존재합니다."); + } } } diff --git a/src/main/java/roomescape/theme/Theme.java b/src/main/java/roomescape/theme/Theme.java index 430a6239..a8d482c7 100644 --- a/src/main/java/roomescape/theme/Theme.java +++ b/src/main/java/roomescape/theme/Theme.java @@ -1,12 +1,23 @@ package roomescape.theme; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Theme { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private String name; - private String description; - public Theme() { - } + private String description; public Theme(Long id, String name, String description) { this.id = id; diff --git a/src/main/java/roomescape/theme/ThemeController.java b/src/main/java/roomescape/theme/ThemeController.java index 03bca41a..9f69c1e9 100644 --- a/src/main/java/roomescape/theme/ThemeController.java +++ b/src/main/java/roomescape/theme/ThemeController.java @@ -1,5 +1,6 @@ package roomescape.theme; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -12,27 +13,25 @@ import java.util.List; @RestController +@RequiredArgsConstructor public class ThemeController { - private ThemeDao themeDao; - public ThemeController(ThemeDao themeDao) { - this.themeDao = themeDao; - } + private final ThemeRepository themeRepository; @PostMapping("/themes") public ResponseEntity createTheme(@RequestBody Theme theme) { - Theme newTheme = themeDao.save(theme); + Theme newTheme = themeRepository.save(theme); return ResponseEntity.created(URI.create("/themes/" + newTheme.getId())).body(newTheme); } @GetMapping("/themes") public ResponseEntity> list() { - return ResponseEntity.ok(themeDao.findAll()); + return ResponseEntity.ok(themeRepository.findAll()); } @DeleteMapping("/themes/{id}") public ResponseEntity deleteTheme(@PathVariable Long id) { - themeDao.deleteById(id); + themeRepository.deleteById(id); return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/roomescape/theme/ThemeDao.java b/src/main/java/roomescape/theme/ThemeDao.java deleted file mode 100644 index 945341d8..00000000 --- a/src/main/java/roomescape/theme/ThemeDao.java +++ /dev/null @@ -1,41 +0,0 @@ -package roomescape.theme; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Repository -public class ThemeDao { - private JdbcTemplate jdbcTemplate; - - public ThemeDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public List findAll() { - return jdbcTemplate.query("SELECT * FROM theme where deleted = false", (rs, rowNum) -> new Theme( - rs.getLong("id"), - rs.getString("name"), - rs.getString("description") - )); - } - - public Theme save(Theme theme) { - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - var ps = connection.prepareStatement("INSERT INTO theme(name, description) VALUES (?, ?)", new String[]{"id"}); - ps.setString(1, theme.getName()); - ps.setString(2, theme.getDescription()); - return ps; - }, keyHolder); - - return new Theme(keyHolder.getKey().longValue(), theme.getName(), theme.getDescription()); - } - - public void deleteById(Long id) { - jdbcTemplate.update("UPDATE theme SET deleted = true WHERE id = ?", id); - } -} diff --git a/src/main/java/roomescape/theme/ThemeRepository.java b/src/main/java/roomescape/theme/ThemeRepository.java new file mode 100644 index 00000000..cbdb21a3 --- /dev/null +++ b/src/main/java/roomescape/theme/ThemeRepository.java @@ -0,0 +1,6 @@ +package roomescape.theme; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ThemeRepository extends JpaRepository { +} diff --git a/src/main/java/roomescape/time/Time.java b/src/main/java/roomescape/time/Time.java index 008ed93c..acb75b80 100644 --- a/src/main/java/roomescape/time/Time.java +++ b/src/main/java/roomescape/time/Time.java @@ -1,7 +1,17 @@ package roomescape.time; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) public class Time { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @Column(name = "time_value") private String value; public Time(Long id, String value) { @@ -13,10 +23,6 @@ public Time(String value) { this.value = value; } - public Time() { - - } - public Long getId() { return id; } diff --git a/src/main/java/roomescape/time/TimeDao.java b/src/main/java/roomescape/time/TimeDao.java deleted file mode 100644 index f39a9a32..00000000 --- a/src/main/java/roomescape/time/TimeDao.java +++ /dev/null @@ -1,41 +0,0 @@ -package roomescape.time; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; - -import java.sql.PreparedStatement; -import java.util.List; - -@Repository -public class TimeDao { - private final JdbcTemplate jdbcTemplate; - - public TimeDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public List