diff --git a/README.md b/README.md index ca861f26e..5dfc70d66 100644 --- a/README.md +++ b/README.md @@ -193,3 +193,164 @@ public class MissionStepTest { } } ``` +## πŸš€ 4단계 - JPA μ „ν™˜ +### μš”κ΅¬μ‚¬ν•­ +- [x] JPAλ₯Ό ν™œμš©ν•˜μ—¬ λ°μ΄ν„°λ² μ΄μŠ€μ— μ ‘κ·Όν•˜λ„λ‘ μˆ˜μ •ν•˜μ„Έμš”. +### μ„ΈλΆ€ μš”κ΅¬μ‚¬ν•­ +#### gradle μ˜μ‘΄μ„± μΆ”κ°€ +- [x] build.gradle νŒŒμΌμ„ μ΄μš©ν•˜μ—¬ λ‹€μŒ μ˜μ‘΄μ„±μ„ λŒ€μ²΄ν•˜μ„Έμš”. + - [x] as is: spring-boot-stater-jdbc + - [x] to be: spring-boot-starter-data-jpa +#### μ—”ν‹°ν‹° λ§€ν•‘ +- [x] λ‹€λ₯Έ 클래슀λ₯Ό μ˜μ‘΄ν•˜μ§€ μ•ŠλŠ” 클래슀 λ¨Όμ € μ—”ν‹°ν‹° 섀정을 ν•˜μ„Έμš”. + - [x] ex) Themeλ‚˜ Time λ“± +#### 연관관계 λ§€ν•‘ +- [x] λ‹€λ₯Έ ν΄λž˜μŠ€μ— μ˜μ‘΄ν•˜λŠ” ν΄λž˜μŠ€λŠ” 연관관계 맀핑을 μΆ”κ°€λ‘œ ν•˜μ„Έμš”. + - [x] ex) Reservation은 Memberλ‚˜ Theme λ“±μ˜ 객체에 μ˜μ‘΄ν•©λ‹ˆλ‹€. +### μš”κ΅¬μ‚¬ν•­ ν…ŒμŠ€νŠΈ +```java +@DataJpaTest +public class JpaTest { + @Autowired + private TestEntityManager entityManager; + + @Autowired + private TimeRepository timeRepository; + + @Test + void 사단계() { + Time time = new Time("10:00"); + entityManager.persist(time); + entityManager.flush(); + + Time persistTime = timeRepository.findById(time.getId()).orElse(null); + + assertThat(persistTime.getTime()).isEqualTo(time.getTime()); + } +} + +``` + +## πŸš€ 5단계 - λ‚΄ μ˜ˆμ•½ λͺ©λ‘ 쑰회 +### μš”κ΅¬μ‚¬ν•­ +- [x] λ‚΄ μ˜ˆμ•½ λͺ©λ‘μ„ μ‘°νšŒν•˜λŠ” APIλ₯Ό κ΅¬ν˜„ν•˜μ„Έμš”. +### λ‚΄ μ˜ˆμ•½ λͺ©λ‘ κΈ°λŠ₯ +- [x] μ•„λž˜μ˜ request와 response μš”κ΅¬μ‚¬ν•­μ— 따라 κΈ°λŠ₯을 κ΅¬ν˜„ν•˜μ„Έμš”. +#### Request +``` +GET /reservations-mine HTTP/1.1 +cookie: token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwibmFtZSI6IuyWtOuTnOuvvCIsInJvbGUiOiJBRE1JTiJ9.vcK93ONRQYPFCxT5KleSM6b7cl1FE-neSLKaFyslsZM +host: localhost:8080 +``` +#### Response +``` +HTTP/1.1 200 +Content-Type: application/json + +[ + { + "reservationId": 1, + "theme": "ν…Œλ§ˆ1", + "date": "2024-03-01", + "time": "10:00", + "status": "μ˜ˆμ•½" + }, + { + "reservationId": 2, + "theme": "ν…Œλ§ˆ2", + "date": "2024-03-01", + "time": "12:00", + "status": "μ˜ˆμ•½" + }, + { + "reservationId": 3, + "theme": "ν…Œλ§ˆ3", + "date": "2024-03-01", + "time": "14:00", + "status": "μ˜ˆμ•½" + } +] + +``` +### μš”κ΅¬μ‚¬ν•­ ν…ŒμŠ€νŠΈ +```java +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) +public class MissionStepTest { + @Test + void μ˜€λ‹¨κ³„() { + String adminToken = createToken("admin@email.com", "password"); + + List reservations = RestAssured.given().log().all() + .cookie("token", adminToken) + .get("/reservations-mine") + .then().log().all() + .statusCode(200) + .extract().jsonPath().getList(".", MyReservationResponse.class); + + assertThat(reservations).hasSize(3); + } +} +``` + +## πŸš€ 6단계 - μ˜ˆμ•½ λŒ€κΈ° κΈ°λŠ₯ +### μš”κ΅¬μ‚¬ν•­ +- [x] μ˜ˆμ•½ λŒ€κΈ° μš”μ²­ κΈ°λŠ₯을 κ΅¬ν˜„ν•˜μ„Έμš”. +- [x] μ˜ˆμ•½ λŒ€κΈ° μ·¨μ†Œ κΈ°λŠ₯도 ν•¨κ»˜ κ΅¬ν˜„ν•˜μ„Έμš”. +- [x] λ‚΄ μ˜ˆμ•½ λͺ©λ‘ 쑰회 μ‹œ μ˜ˆμ•½ λŒ€κΈ° λͺ©λ‘λ„ ν•¨κ»˜ ν¬ν•¨ν•˜μ„Έμš”. +- [x] 쀑볡 μ˜ˆμ•½μ΄ λΆˆκ°€λŠ₯ ν•˜λ„λ‘ κ΅¬ν˜„ν•˜μ„Έμš”. + +> ⚠️ 심화 μš”κ΅¬μ‚¬ν•­ - λ‚΄ μ˜ˆμ•½ λͺ©λ‘μ˜ μ˜ˆμ•½ λŒ€κΈ° μƒνƒœμ— λͺ‡ 번째 λŒ€κΈ°μΈμ§€λ„ ν•¨κ»˜ ν‘œμ‹œν•˜μ„Έμš”. +#### μ˜ˆμ•½ λŒ€κΈ° μš”μ²­ +![img_1.png](img_1.png) +#### λ‚΄ μ˜ˆμ•½ λͺ©λ‘μ—μ„œ 쑰회 & μ˜ˆμ•½ λŒ€κΈ° μ·¨μ†Œ +![img_2.png](img_2.png) +### μš”κ΅¬μ‚¬ν•­ ν…ŒμŠ€νŠΈ +```java +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) +public class MissionStepTest { + @Test + void μœ‘λ‹¨κ³„() { + String brownToken = createToken("brown@email.com", "password"); + + Map params = new HashMap<>(); + params.put("date", "2024-03-01"); + params.put("time", "1"); + params.put("theme", "1"); + + // μ˜ˆμ•½ λŒ€κΈ° 생성 + WaitingResponse waiting = RestAssured.given().log().all() + .body(params) + .cookie("token", brownToken) + .contentType(ContentType.JSON) + .post("/waitings") + .then().log().all() + .statusCode(201) + .extract().as(WaitingResponse.class); + + // λ‚΄ μ˜ˆμ•½ λͺ©λ‘ 쑰회 + List myReservations = RestAssured.given().log().all() + .body(params) + .cookie("token", brownToken) + .contentType(ContentType.JSON) + .get("/reservations-mine") + .then().log().all() + .statusCode(200) + .extract().jsonPath().getList(".", MyReservationResponse.class); + + // μ˜ˆμ•½ λŒ€κΈ° μƒνƒœ 확인 + String status = myReservations.stream() + .filter(it -> it.getId() == waiting.getId()) + .filter(it -> !it.getStatus().equals("μ˜ˆμ•½")) + .findFirst() + .map(it -> it.getStatus()) + .orElse(null); + + assertThat(status).isEqualTo("1번째 μ˜ˆμ•½λŒ€κΈ°"); + } +} + + +``` + diff --git a/build.gradle b/build.gradle index 8d52aebc6..9bc129a0e 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 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.0.0' diff --git a/img_1.png b/img_1.png new file mode 100644 index 000000000..c1c0b69c4 Binary files /dev/null and b/img_1.png differ diff --git a/img_2.png b/img_2.png new file mode 100644 index 000000000..d27d52213 Binary files /dev/null and b/img_2.png differ diff --git a/src/main/java/roomescape/auth/AuthService.java b/src/main/java/roomescape/auth/AuthService.java index f48933bbf..5b77d4d08 100644 --- a/src/main/java/roomescape/auth/AuthService.java +++ b/src/main/java/roomescape/auth/AuthService.java @@ -4,28 +4,31 @@ import roomescape.auth.dto.LoginMember; import roomescape.auth.dto.LoginRequest; import roomescape.auth.jwt.TokenProvider; +import roomescape.exception.AuthorizationException; import roomescape.member.Member; -import roomescape.member.MemberDao; +import roomescape.member.MemberRepository; @Service public class AuthService { - private final MemberDao memberDao; + private final MemberRepository memberRepository; private final TokenProvider tokenProvider; - public AuthService(MemberDao memberDao, TokenProvider tokenProvider) { - this.memberDao = memberDao; + public AuthService(MemberRepository memberRepository, TokenProvider tokenProvider) { + this.memberRepository = memberRepository; this.tokenProvider = tokenProvider; } public String createToken(LoginRequest loginRequest) { - Member member = memberDao.findByEmailAndPassword(loginRequest.getEmail(), - loginRequest.getPassword()); + Member member = memberRepository.findByEmailAndPassword(loginRequest.getEmail(), + loginRequest.getPassword()) + .orElseThrow(() -> new AuthorizationException("μ‚¬μš©μžλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")); return tokenProvider.createToken(member); } public LoginMember login(String email, String password) { - Member member = memberDao.findByEmailAndPassword(email, password); + Member member = memberRepository.findByEmailAndPassword(email, password) + .orElseThrow(() -> new AuthorizationException("μ‚¬μš©μžλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")); return new LoginMember( member.getId(), member.getName(), diff --git a/src/main/java/roomescape/member/Member.java b/src/main/java/roomescape/member/Member.java index 3e0dfc106..e95210422 100644 --- a/src/main/java/roomescape/member/Member.java +++ b/src/main/java/roomescape/member/Member.java @@ -1,6 +1,14 @@ package roomescape.member; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity public class Member { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String email; @@ -29,6 +37,9 @@ public Member(String name, String email, String password, String role) { this.role = role; } + protected Member() { + } + public Long getId() { return id; } diff --git a/src/main/java/roomescape/member/MemberDao.java b/src/main/java/roomescape/member/MemberDao.java deleted file mode 100644 index 5256a5935..000000000 --- a/src/main/java/roomescape/member/MemberDao.java +++ /dev/null @@ -1,75 +0,0 @@ -package roomescape.member; - -import java.util.List; -import java.util.Optional; -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(), - member.getRole()); - } - - 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 - ); - } - - public Optional findById(Long id) { - List result = jdbcTemplate.query( - "SELECT id, name, email, password, role FROM member WHERE id = ?", - (rs, rowNum) -> new Member( - rs.getLong("id"), - rs.getString("name"), - rs.getString("email"), - rs.getString("password"), - rs.getString("role") - ), - id - ); - return result.stream().findAny(); - } -} diff --git a/src/main/java/roomescape/member/MemberRepository.java b/src/main/java/roomescape/member/MemberRepository.java new file mode 100644 index 000000000..32b482dcc --- /dev/null +++ b/src/main/java/roomescape/member/MemberRepository.java @@ -0,0 +1,10 @@ +package roomescape.member; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface MemberRepository extends JpaRepository { + Optional findByEmailAndPassword(String email, String password); +} diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index dd8dea76a..b0c40075e 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -4,15 +4,16 @@ @Service public class MemberService { - private MemberDao memberDao; + private final MemberRepository memberRepository; - public MemberService(MemberDao memberDao) { - this.memberDao = memberDao; + public MemberService(MemberRepository memberRepository) { + this.memberRepository = memberRepository; } public MemberResponse createMember(MemberRequest memberRequest) { - Member member = memberDao.save(new Member(memberRequest.getName(), memberRequest.getEmail(), - memberRequest.getPassword(), "USER")); + Member member = memberRepository.save( + new Member(memberRequest.getName(), memberRequest.getEmail(), + memberRequest.getPassword(), "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 000000000..485053e33 --- /dev/null +++ b/src/main/java/roomescape/reservation/MyReservationResponse.java @@ -0,0 +1,31 @@ +package roomescape.reservation; + +import roomescape.waiting.Waiting; + +public record MyReservationResponse( + Long id, + String theme, + String date, + String time, + String status +) { + public static MyReservationResponse from(Reservation reservation) { + return new MyReservationResponse( + reservation.getId(), + reservation.getTheme().getName(), + reservation.getDate(), + reservation.getTime().getTime(), + "μ˜ˆμ•½" + ); + } + + public static MyReservationResponse from(Waiting waiting, Long rank) { + return new MyReservationResponse( + waiting.getId(), + waiting.getTheme().getName(), + waiting.getDate(), + waiting.getTime().getTime(), + (rank + 1) + "번째 μ˜ˆμ•½λŒ€κΈ°" + ); + } +} diff --git a/src/main/java/roomescape/reservation/Reservation.java b/src/main/java/roomescape/reservation/Reservation.java index 83a7edf1b..27bbab185 100644 --- a/src/main/java/roomescape/reservation/Reservation.java +++ b/src/main/java/roomescape/reservation/Reservation.java @@ -1,32 +1,52 @@ package roomescape.reservation; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import roomescape.member.Member; import roomescape.theme.Theme; import roomescape.time.Time; +@Entity public class Reservation { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String date; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "time_id") private Time time; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "theme_id") private Theme theme; - public Reservation(Long id, String name, String date, Time time, Theme theme) { - this.id = id; + public Reservation(String name, String date, Time time, Theme theme) { this.name = name; this.date = date; this.time = time; this.theme = theme; } - public Reservation(String name, String date, Time time, Theme theme) { + public Reservation(String name, String date, Member member, Time time, Theme theme) { this.name = name; this.date = date; + this.member = member; this.time = time; this.theme = theme; } - public Reservation() { - + protected Reservation() { } public Long getId() { @@ -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 5a5812644..ad33940fe 100644 --- a/src/main/java/roomescape/reservation/ReservationController.java +++ b/src/main/java/roomescape/reservation/ReservationController.java @@ -25,8 +25,16 @@ public List list() { return reservationService.findAll(); } + @GetMapping("/reservations-mine") + public ResponseEntity> getMyReservations(LoginMember loginMember) { + List reservations = reservationService.findReservationsByMemberId( + loginMember.id()); + return ResponseEntity.ok(reservations); + } + @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, + public ResponseEntity create( + @RequestBody ReservationRequest reservationRequest, LoginMember member) { if (reservationRequest.getDate() == null || reservationRequest.getTheme() == null diff --git a/src/main/java/roomescape/reservation/ReservationDao.java b/src/main/java/roomescape/reservation/ReservationDao.java deleted file mode 100644 index d6902e68c..000000000 --- a/src/main/java/roomescape/reservation/ReservationDao.java +++ /dev/null @@ -1,132 +0,0 @@ -package roomescape.reservation; - -import java.sql.PreparedStatement; -import java.util.List; -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; - -@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 000000000..262ec5d66 --- /dev/null +++ b/src/main/java/roomescape/reservation/ReservationRepository.java @@ -0,0 +1,16 @@ +package roomescape.reservation; + +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import roomescape.member.Member; +import roomescape.theme.Theme; +import roomescape.time.Time; + +public interface ReservationRepository extends JpaRepository { + List findByDateAndThemeId(String date, Long themeId); + + List findByMemberId(Long memberId); + + boolean existsByMemberAndDateAndTimeAndTheme(Member member, String date, Time time, + Theme theme); +} diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java index 0fde10272..c3a9f93b6 100644 --- a/src/main/java/roomescape/reservation/ReservationService.java +++ b/src/main/java/roomescape/reservation/ReservationService.java @@ -1,52 +1,110 @@ package roomescape.reservation; +import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import roomescape.auth.dto.LoginMember; +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; @Service public class ReservationService { - private final ReservationDao reservationDao; + private final ReservationRepository reservationRepository; + private final ThemeRepository themeRepository; + private final TimeRepository timeRepository; + private final MemberRepository memberRepository; + private final WaitingRepository waitingRepository; - public ReservationService(ReservationDao reservationDao) { - this.reservationDao = reservationDao; + public ReservationService(ReservationRepository reservationRepository, + ThemeRepository themeRepository, TimeRepository timeRepository, + MemberRepository memberRepository, WaitingRepository waitingRepository) { + this.reservationRepository = reservationRepository; + this.themeRepository = themeRepository; + this.timeRepository = timeRepository; + this.memberRepository = memberRepository; + this.waitingRepository = waitingRepository; } - public ReservationResponse save(ReservationRequest reservationRequest) { - Reservation reservation = reservationDao.save(reservationRequest); + @Transactional + public ReservationResponse save(ReservationRequest request) { + Theme theme = themeRepository.findById(request.getTheme()) + .orElseThrow( + () -> new IllegalArgumentException("μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” ν…Œλ§ˆμž…λ‹ˆλ‹€. id=" + request.getTheme())); + Time time = timeRepository.findById(request.getTime()) + .orElseThrow( + () -> new IllegalArgumentException("μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ‹œκ°„μž…λ‹ˆλ‹€. id=" + request.getTime())); - return new ReservationResponse(reservation.getId(), reservationRequest.getName(), - reservation.getTheme().getName(), reservation.getDate(), - reservation.getTime().getValue()); + Reservation reservation = new Reservation( + request.getName(), + request.getDate(), + time, + theme + ); + + Reservation saved = reservationRepository.save(reservation); + + return new ReservationResponse(saved.getId(), saved.getName(), + saved.getTheme().getName(), saved.getDate(), + saved.getTime().getTime()); } - public ReservationResponse save(ReservationRequest request, LoginMember member) { - String name = request.getName(); + @Transactional + public ReservationResponse save(ReservationRequest request, LoginMember loginMember) { + Theme theme = themeRepository.findById(request.getTheme()) + .orElseThrow( + () -> new IllegalArgumentException("μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” ν…Œλ§ˆμž…λ‹ˆλ‹€. id=" + request.getTheme())); + Time time = timeRepository.findById(request.getTime()) + .orElseThrow( + () -> new IllegalArgumentException("μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ‹œκ°„μž…λ‹ˆλ‹€. id=" + request.getTime())); + Member member = memberRepository.findById(loginMember.id()) + .orElseThrow( + () -> new IllegalArgumentException("μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” νšŒμ›μž…λ‹ˆλ‹€. id=" + loginMember.id())); + String name = request.getName(); if (name == null || name.isBlank()) { - name = member.name(); - request.setName(name); + name = loginMember.name(); } - Reservation reservation = reservationDao.save(request); + Reservation reservation = new Reservation(name, request.getDate(), member, time, theme); + Reservation saved = reservationRepository.save(reservation); - return new ReservationResponse( - reservation.getId(), - name, - reservation.getTheme().getName(), - reservation.getDate(), - reservation.getTime().getValue() - ); + return new ReservationResponse(saved.getId(), saved.getName(), + saved.getTheme().getName(), saved.getDate(), saved.getTime().getTime()); } + @Transactional public void deleteById(Long id) { - reservationDao.deleteById(id); + reservationRepository.deleteById(id); } public List findAll() { - return reservationDao.findAll().stream() + return reservationRepository.findAll().stream() .map(it -> new ReservationResponse(it.getId(), it.getName(), it.getTheme().getName(), - it.getDate(), it.getTime().getValue())) + it.getDate(), it.getTime().getTime())) .toList(); } + + public List findReservationsByMemberId(Long memberId) { + List reservations = reservationRepository.findByMemberId(memberId); + + List waitings = waitingRepository.findWaitingsWithRankByMemberId(memberId); + + List result = new ArrayList<>(); + result.addAll(reservations.stream() + .map(MyReservationResponse::from) + .toList()); + + result.addAll(waitings.stream() + .map(waitingWithRank -> MyReservationResponse.from(waitingWithRank.getWaiting(), + waitingWithRank.getRank())).toList()); + + return result; + } } diff --git a/src/main/java/roomescape/theme/Theme.java b/src/main/java/roomescape/theme/Theme.java index 430a6239c..9a18b7344 100644 --- a/src/main/java/roomescape/theme/Theme.java +++ b/src/main/java/roomescape/theme/Theme.java @@ -1,11 +1,19 @@ package roomescape.theme; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity public class Theme { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String description; - public Theme() { + protected Theme() { } public Theme(Long id, String name, String description) { diff --git a/src/main/java/roomescape/theme/ThemeController.java b/src/main/java/roomescape/theme/ThemeController.java index fa7aef080..2cbb79a51 100644 --- a/src/main/java/roomescape/theme/ThemeController.java +++ b/src/main/java/roomescape/theme/ThemeController.java @@ -12,26 +12,33 @@ @RestController public class ThemeController { - private ThemeDao themeDao; + private final ThemeRepository themeRepository; - public ThemeController(ThemeDao themeDao) { - this.themeDao = themeDao; + public ThemeController(ThemeRepository themeRepository) { + this.themeRepository = themeRepository; } @PostMapping("/themes") - public ResponseEntity createTheme(@RequestBody Theme theme) { - Theme newTheme = themeDao.save(theme); - return ResponseEntity.created(URI.create("/themes/" + newTheme.getId())).body(newTheme); + public ResponseEntity createTheme(@RequestBody ThemeRequest request) { + Theme newTheme = themeRepository.save(new Theme(request.name(), request.description())); + return ResponseEntity.created(URI.create("/themes/" + newTheme.getId())) + .body(ThemeResponse.from(newTheme)); } @GetMapping("/themes") - public ResponseEntity> list() { - return ResponseEntity.ok(themeDao.findAll()); + public ResponseEntity> list() { + List result = themeRepository.findAll().stream() + .map(ThemeResponse::from) + .toList(); + return ResponseEntity.ok(result); } @DeleteMapping("/themes/{id}") public ResponseEntity deleteTheme(@PathVariable Long id) { - themeDao.deleteById(id); + if (!themeRepository.existsById(id)) { + return ResponseEntity.notFound().build(); + } + 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 bd4cc55bf..000000000 --- a/src/main/java/roomescape/theme/ThemeDao.java +++ /dev/null @@ -1,42 +0,0 @@ -package roomescape.theme; - -import java.util.List; -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 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 000000000..304cb0ef1 --- /dev/null +++ b/src/main/java/roomescape/theme/ThemeRepository.java @@ -0,0 +1,8 @@ +package roomescape.theme; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ThemeRepository extends JpaRepository { +} diff --git a/src/main/java/roomescape/theme/ThemeRequest.java b/src/main/java/roomescape/theme/ThemeRequest.java new file mode 100644 index 000000000..4acfab8b9 --- /dev/null +++ b/src/main/java/roomescape/theme/ThemeRequest.java @@ -0,0 +1,4 @@ +package roomescape.theme; + +public record ThemeRequest(String name, String description) { +} diff --git a/src/main/java/roomescape/theme/ThemeResponse.java b/src/main/java/roomescape/theme/ThemeResponse.java new file mode 100644 index 000000000..99fc401d1 --- /dev/null +++ b/src/main/java/roomescape/theme/ThemeResponse.java @@ -0,0 +1,7 @@ +package roomescape.theme; + +public record ThemeResponse(Long id, String name, String description) { + public static ThemeResponse from(Theme theme) { + return new ThemeResponse(theme.getId(), theme.getName(), theme.getDescription()); + } +} diff --git a/src/main/java/roomescape/time/Time.java b/src/main/java/roomescape/time/Time.java index 008ed93cf..30ade3515 100644 --- a/src/main/java/roomescape/time/Time.java +++ b/src/main/java/roomescape/time/Time.java @@ -1,27 +1,37 @@ package roomescape.time; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity public class Time { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String value; - public Time(Long id, String value) { + @Column(name = "time_value") + private String time; + + public Time(Long id, String time) { this.id = id; - this.value = value; + this.time = time; } - public Time(String value) { - this.value = value; + public Time(String time) { + this.time = time; } - public Time() { - + protected Time() { } public Long getId() { return id; } - public String getValue() { - return value; + public String getTime() { + return time; } } diff --git a/src/main/java/roomescape/time/TimeController.java b/src/main/java/roomescape/time/TimeController.java index 2343114d1..8b026c23d 100644 --- a/src/main/java/roomescape/time/TimeController.java +++ b/src/main/java/roomescape/time/TimeController.java @@ -1,5 +1,7 @@ package roomescape.time; +import java.net.URI; +import java.util.List; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -9,9 +11,6 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.net.URI; -import java.util.List; - @RestController public class TimeController { private TimeService timeService; @@ -27,7 +26,7 @@ public List