diff --git a/build.gradle b/build.gradle index 8d52aebc6..a3dcc18ae 100644 --- a/build.gradle +++ b/build.gradle @@ -15,13 +15,13 @@ 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 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.0.0' implementation 'io.jsonwebtoken:jjwt-api:0.11.2' implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' implementation 'io.jsonwebtoken:jjwt-gson:0.11.2' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' diff --git a/src/main/java/roomescape/auth/AuthInterceptor.java b/src/main/java/roomescape/auth/AuthInterceptor.java new file mode 100644 index 000000000..6d1f26a19 --- /dev/null +++ b/src/main/java/roomescape/auth/AuthInterceptor.java @@ -0,0 +1,90 @@ +package roomescape.auth; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.io.IOException; +import java.util.Arrays; + +public class AuthInterceptor implements HandlerInterceptor { + + private final AuthService authService; + + public AuthInterceptor(AuthService authService) { + this.authService = authService; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + ResponseEntity authCheck = checkAuthentication(request); + Cookie[] cookies = request.getCookies(); + if (authCheck != null) { + handleResponseEntity(response, authCheck); + return false; + } + + LoginResponse loginResponse = getLoginResponse(request); + + if (loginResponse == null) { + ResponseEntity loginRedirect = ResponseEntity.status(HttpStatus.FOUND) + .header(HttpHeaders.LOCATION, "/login") + .body("유저를 찾을 수 없습니다. 로그인 페이지로 이동합니다."); + handleResponseEntity(response, loginRedirect); + return false; + } + if ("ADMIN".equals(loginResponse.getRole())) { + return true; // 관리자 페이지 접근 허용 + } else { + ResponseEntity accessDenied = ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .header(HttpHeaders.LOCATION, "/access-denied") + .body("관리자 페이지입니다. 접근 권한이 없습니다."); + handleResponseEntity(response, accessDenied); + return false; + } + } + + private ResponseEntity checkAuthentication(HttpServletRequest request) { + Cookie[] cookies = request.getCookies(); + if (cookies == null) { + return ResponseEntity.status(HttpStatus.FOUND) + .header(HttpHeaders.LOCATION, "/login") + .body("쿠키를 찾을 수 없습니다. 로그인 페이지로 이동합니다."); + } + + Cookie tokenCookie = Arrays.stream(cookies) + .filter(cookie -> "token".equals(cookie.getName())) + .findFirst() + .orElse(null); + + if (tokenCookie == null) { + return ResponseEntity.status(HttpStatus.FOUND) + .header(HttpHeaders.LOCATION, "/login") + .body("토큰을 찾을 수 없습니다. 로그인 페이지로 이동합니다."); + } + return null; + } + + private LoginResponse getLoginResponse(HttpServletRequest request) { + Cookie tokenCookie = Arrays.stream(request.getCookies()) + .filter(cookie -> "token".equals(cookie.getName())) + .findFirst() + .orElse(null); + + if (tokenCookie == null) { + return null; + } + + return authService.findUserByToken(tokenCookie.getValue()); + } + + private void handleResponseEntity(HttpServletResponse response, ResponseEntity entity) throws IOException, IOException { + response.setStatus(entity.getStatusCodeValue()); + response.setHeader(HttpHeaders.LOCATION, entity.getHeaders().getFirst(HttpHeaders.LOCATION)); + response.getWriter().write(entity.getBody()); + } +} diff --git a/src/main/java/roomescape/auth/AuthInterceptorConfig.java b/src/main/java/roomescape/auth/AuthInterceptorConfig.java new file mode 100644 index 000000000..c862f4ecb --- /dev/null +++ b/src/main/java/roomescape/auth/AuthInterceptorConfig.java @@ -0,0 +1,19 @@ +package roomescape.auth; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AuthInterceptorConfig { + + private final AuthService authService; + + public AuthInterceptorConfig(AuthService authService) { + this.authService = authService; + } + + @Bean + public AuthInterceptor authInterceptor() { + return new AuthInterceptor(authService); + } +} diff --git a/src/main/java/roomescape/auth/AuthService.java b/src/main/java/roomescape/auth/AuthService.java new file mode 100644 index 000000000..c8b762bd9 --- /dev/null +++ b/src/main/java/roomescape/auth/AuthService.java @@ -0,0 +1,70 @@ +package roomescape.auth; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import org.springframework.stereotype.Service; + +import java.security.Key; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Service +public class AuthService { + + private static final Key SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256); + private static final long EXPIRATION_TIME = 86400000; + + public TokenResponse createToken(TokenRequest tokenRequest) { + + String role = "USER"; + if ("admin@email.com".equals(tokenRequest.getEmail())) { + role = "ADMIN"; + } + + Map claims = new HashMap<>(); + claims.put("role", role); + + String token = Jwts.builder() + .setClaims(claims) + .setSubject(tokenRequest.getEmail()) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) + .signWith(SECRET_KEY) + .compact(); + + return new TokenResponse(token); + } + + public LoginResponse findUserByToken(String token) { + + String role = Jwts.parserBuilder() + .setSigningKey(SECRET_KEY) + .build() + .parseClaimsJws(token) + .getBody() + .get("role", String.class); + String email = Jwts.parserBuilder() + .setSigningKey(SECRET_KEY) + .build() + .parseClaimsJws(token) + .getBody() + .getSubject(); + + return new LoginResponse(email, role); + } + + public UserResponse checkUserByToken(String token) { + Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); + String email = claims.getSubject(); + if ("admin@email.com".equals(email)) { + return new UserResponse("어드민", 1L); + } else if ("brown@email.com".equals(email)) { + return new UserResponse("brown",2L); + } else { + return null; + } + } +} diff --git a/src/main/java/roomescape/auth/LoginController.java b/src/main/java/roomescape/auth/LoginController.java new file mode 100644 index 000000000..ce3d7cc81 --- /dev/null +++ b/src/main/java/roomescape/auth/LoginController.java @@ -0,0 +1,58 @@ +package roomescape.auth; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CookieValue; +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.RestController; + +@RestController +public class LoginController { + private final AuthService authService; + + public LoginController(AuthService authService) { + this.authService = authService; + } + + @PostMapping("/login") + public ResponseEntity login(@RequestBody TokenRequest tokenRequest, HttpServletResponse response) { + TokenResponse tokenResponse = authService.createToken(tokenRequest); + + response.addCookie(createHttpOnlyCookie("token", tokenResponse.getAccessToken())); + + return ResponseEntity.ok().build(); + } + + private Cookie createHttpOnlyCookie(String name, String value) { + Cookie cookie = new Cookie(name, value); + cookie.setPath("/"); + cookie.setHttpOnly(true); + return cookie; + } + + @PostMapping("/logout") + public ResponseEntity logout(HttpServletResponse response) { + Cookie cookie = new Cookie("token", ""); + cookie.setHttpOnly(true); + cookie.setPath("/"); + cookie.setMaxAge(0); + response.addCookie(cookie); + return ResponseEntity.ok().build(); + } + + @GetMapping("/login/check") + public ResponseEntity checkLogin(@CookieValue("token") String token) { + UserResponse user = authService.checkUserByToken(token); + if (user == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + return ResponseEntity.ok(user); + } +} diff --git a/src/main/java/roomescape/auth/LoginReservationResponse.java b/src/main/java/roomescape/auth/LoginReservationResponse.java new file mode 100644 index 000000000..b7e3f4f82 --- /dev/null +++ b/src/main/java/roomescape/auth/LoginReservationResponse.java @@ -0,0 +1,14 @@ +package roomescape.auth; + +public record LoginReservationResponse( + String name +) { + public LoginReservationResponse(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} + diff --git a/src/main/java/roomescape/auth/LoginResponse.java b/src/main/java/roomescape/auth/LoginResponse.java new file mode 100644 index 000000000..d924cd5a1 --- /dev/null +++ b/src/main/java/roomescape/auth/LoginResponse.java @@ -0,0 +1,19 @@ +package roomescape.auth; + +public record LoginResponse( + String email, + String role +) { + public LoginResponse(String email, String role) { + this.email = email; + this.role = role; + } + + public String getEmail() { + return email; + } + + public String getRole() { + return role; + } +} diff --git a/src/main/java/roomescape/auth/TokenRequest.java b/src/main/java/roomescape/auth/TokenRequest.java new file mode 100644 index 000000000..99ee9641d --- /dev/null +++ b/src/main/java/roomescape/auth/TokenRequest.java @@ -0,0 +1,15 @@ +package roomescape.auth; + +public record TokenRequest( + String email, + String password +) { + public TokenRequest(String email, String password) { + this.email = email; + this.password = password; + } + + public String getEmail() { + return email; + } +} diff --git a/src/main/java/roomescape/auth/TokenResponse.java b/src/main/java/roomescape/auth/TokenResponse.java new file mode 100644 index 000000000..ffd5316f1 --- /dev/null +++ b/src/main/java/roomescape/auth/TokenResponse.java @@ -0,0 +1,13 @@ +package roomescape.auth; + +public record TokenResponse( + String accessToken +) { + public TokenResponse(String accessToken) { + this.accessToken = accessToken; + } + + public String getAccessToken() { + return accessToken; + } +} diff --git a/src/main/java/roomescape/auth/UserResponse.java b/src/main/java/roomescape/auth/UserResponse.java new file mode 100644 index 000000000..6d65f065b --- /dev/null +++ b/src/main/java/roomescape/auth/UserResponse.java @@ -0,0 +1,22 @@ +package roomescape.auth; + +public record UserResponse( + String name, + long id +) { + public UserResponse(String name, long id) { + this.name = name; + this.id = id; + + } + + public String getName() { + return name; + + } + public long getId() { + return id; + + } + +} diff --git a/src/main/java/roomescape/auth/WebConfig.java b/src/main/java/roomescape/auth/WebConfig.java new file mode 100644 index 000000000..5d423d082 --- /dev/null +++ b/src/main/java/roomescape/auth/WebConfig.java @@ -0,0 +1,19 @@ +package roomescape.auth; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Autowired + private AuthInterceptor authInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(authInterceptor) + .addPathPatterns("/admin/**"); + } +} diff --git a/src/main/java/roomescape/member/Member.java b/src/main/java/roomescape/member/Member.java index 903aaa9b0..d50fda2c6 100644 --- a/src/main/java/roomescape/member/Member.java +++ b/src/main/java/roomescape/member/Member.java @@ -1,6 +1,13 @@ package roomescape.member; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +@Entity public class Member { + @Id + @GeneratedValue(strategy = jakarta.persistence.GenerationType.IDENTITY) private Long id; private String name; private String email; @@ -21,6 +28,10 @@ public Member(String name, String email, String password, String role) { this.role = role; } + public Member() { + + } + public Long getId() { return id; } diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 881ae5e0d..d0b823c63 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -8,6 +8,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import roomescape.auth.TokenRequest; +import roomescape.auth.TokenResponse; import java.net.URI; @@ -25,13 +27,6 @@ public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member); } - @PostMapping("/logout") - public ResponseEntity logout(HttpServletResponse response) { - Cookie cookie = new Cookie("token", ""); - cookie.setHttpOnly(true); - cookie.setPath("/"); - cookie.setMaxAge(0); - response.addCookie(cookie); - return ResponseEntity.ok().build(); - } + + } diff --git a/src/main/java/roomescape/member/MemberDao.java b/src/main/java/roomescape/member/MemberDao.java deleted file mode 100644 index 81f77f4cd..000000000 --- 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 000000000..6f8b8f64a --- /dev/null +++ b/src/main/java/roomescape/member/MemberRepository.java @@ -0,0 +1,9 @@ +package roomescape.member; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface MemberRepository extends JpaRepository { + Member findMemberByName(String name); +} diff --git a/src/main/java/roomescape/member/MemberRequest.java b/src/main/java/roomescape/member/MemberRequest.java index cafb79f14..efa9223a7 100644 --- a/src/main/java/roomescape/member/MemberRequest.java +++ b/src/main/java/roomescape/member/MemberRequest.java @@ -1,10 +1,10 @@ package roomescape.member; -public class MemberRequest { - private String name; - private String email; - private String password; - +public record MemberRequest( + String name, + String email, + String password +) { public String getName() { return name; } diff --git a/src/main/java/roomescape/member/MemberResponse.java b/src/main/java/roomescape/member/MemberResponse.java index b9fa3b97a..e880c56f0 100644 --- a/src/main/java/roomescape/member/MemberResponse.java +++ b/src/main/java/roomescape/member/MemberResponse.java @@ -1,10 +1,10 @@ package roomescape.member; -public class MemberResponse { - private Long id; - private String name; - private String email; - +public record MemberResponse( + Long id, + String name, + String email +) { public MemberResponse(Long id, String name, String email) { this.id = id; this.name = name; diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index ccaa8cba5..03bd4cbc6 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -4,14 +4,14 @@ @Service public class MemberService { - private MemberDao memberDao; + private 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..5be4e1bf7 --- /dev/null +++ b/src/main/java/roomescape/reservation/MyReservationResponse.java @@ -0,0 +1,25 @@ +package roomescape.reservation; + +public record MyReservationResponse( + Long id, + String theme, + String date, + String time, + String status +) { + public MyReservationResponse(Long id, String theme, String date, String time, String status) { + this.id = id; + this.theme = theme; + this.date = date; + this.time = time; + this.status = status; + } + + public Long getId() { + return id; + } + + public String getStatus() { + return status; + } +} diff --git a/src/main/java/roomescape/reservation/Reservation.java b/src/main/java/roomescape/reservation/Reservation.java index 83a7edf1b..fc4e67772 100644 --- a/src/main/java/roomescape/reservation/Reservation.java +++ b/src/main/java/roomescape/reservation/Reservation.java @@ -1,15 +1,34 @@ package roomescape.reservation; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +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 = jakarta.persistence.GenerationType.IDENTITY) private Long id; private String name; private String date; + + @ManyToOne + @JoinColumn(name = "time_id") private Time time; + + @ManyToOne + @JoinColumn(name = "theme_id") private Theme theme; + @ManyToOne + @JoinColumn(name = "member_id") + private Member member; + public Reservation(Long id, String name, String date, Time time, Theme theme) { this.id = id; this.name = name; diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index b3bef3990..4669804b0 100644 --- a/src/main/java/roomescape/reservation/ReservationController.java +++ b/src/main/java/roomescape/reservation/ReservationController.java @@ -1,5 +1,7 @@ package roomescape.reservation; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -7,17 +9,23 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import roomescape.auth.AuthService; + -import java.net.URI; import java.util.List; +import java.util.Map; +import roomescape.auth.UserResponse; +import roomescape.waiting.WaitingResponse; @RestController public class ReservationController { private final ReservationService reservationService; + private final AuthService authService; - public ReservationController(ReservationService reservationService) { + public ReservationController(ReservationService reservationService, AuthService authService) { this.reservationService = reservationService; + this.authService = authService; } @GetMapping("/reservations") @@ -25,17 +33,85 @@ public List list() { return reservationService.findAll(); } + @GetMapping("/reservations-mine") + public List listMine(HttpServletRequest request) { + String token = extractTokenFromCookies(request.getCookies()); + + if (token == null) { + return List.of(); + } + + UserResponse userResponse = authService.checkUserByToken(token); + if (userResponse == null) { + return List.of(); + } + + return reservationService.findMyReservations(userResponse.getName()); + } + + private String extractTokenFromCookies(Cookie[] cookies) { + if (cookies != null) { + for (Cookie cookie : cookies) { + if ("token".equals(cookie.getName())) { + return cookie.getValue(); + } + } + } + return null; + } + + @PostMapping("/waitings") + public ResponseEntity createWaiting(@RequestBody Map waitingRequest, HttpServletRequest request) { + String token = extractTokenFromCookies(request.getCookies()); + + if (token == null) { + return ResponseEntity.status(401).build(); + } + + UserResponse userResponse = authService.checkUserByToken(token); + if (userResponse == null) { + return ResponseEntity.status(401).build(); + } + + WaitingResponse waitingResponse = reservationService.createWaiting( + waitingRequest.get("date"), + waitingRequest.get("time"), + waitingRequest.get("theme"), + userResponse.getId() + ); + + return ResponseEntity.status(201).body(waitingResponse); + } + @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest reservationRequest) { - if (reservationRequest.getName() == null - || reservationRequest.getDate() == null - || reservationRequest.getTheme() == null - || reservationRequest.getTime() == null) { - return ResponseEntity.badRequest().build(); + public ResponseEntity makeReservation(@RequestBody ReservationRequest reservationRequest, HttpServletRequest request) { + String token = null; + + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) { + if ("token".equals(cookie.getName())) { + token = cookie.getValue(); + break; + } + } } - ReservationResponse reservation = reservationService.save(reservationRequest); - return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation); + if (token == null) { + return ResponseEntity.status(401).build(); + } + + UserResponse userResponse = authService.checkUserByToken(token); + + ReservationResponse reservationResponse = new ReservationResponse( + 1L, + reservationRequest.getName() != null ? reservationRequest.getName() : userResponse.getName(), + reservationRequest.getTheme(), + reservationRequest.getDate(), + reservationRequest.getTime() + ); + + return ResponseEntity.status(201).body(reservationResponse); } @DeleteMapping("/reservations/{id}") @@ -43,4 +119,6 @@ public ResponseEntity delete(@PathVariable Long id) { reservationService.deleteById(id); return ResponseEntity.noContent().build(); } + + } diff --git a/src/main/java/roomescape/reservation/ReservationDao.java b/src/main/java/roomescape/reservation/ReservationDao.java deleted file mode 100644 index a4972430c..000000000 --- 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 000000000..8c01140c6 --- /dev/null +++ b/src/main/java/roomescape/reservation/ReservationRepository.java @@ -0,0 +1,12 @@ +package roomescape.reservation; + +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ReservationRepository extends JpaRepository { + List findByDateAndThemeId(String date, Long themeId); + + List findMyReservationsByName(String memberName); +} diff --git a/src/main/java/roomescape/reservation/ReservationRequest.java b/src/main/java/roomescape/reservation/ReservationRequest.java index 19f441246..f5bdbf64e 100644 --- a/src/main/java/roomescape/reservation/ReservationRequest.java +++ b/src/main/java/roomescape/reservation/ReservationRequest.java @@ -1,11 +1,11 @@ package roomescape.reservation; -public class ReservationRequest { - private String name; - private String date; - private Long theme; - private Long time; - +public record ReservationRequest( + String name, + String date, + String theme, + String time +) { public String getName() { return name; } @@ -14,11 +14,11 @@ public String getDate() { return date; } - public Long getTheme() { + public String getTheme() { return theme; } - public Long getTime() { + public String getTime() { return time; } } diff --git a/src/main/java/roomescape/reservation/ReservationResponse.java b/src/main/java/roomescape/reservation/ReservationResponse.java index 41360a363..8bf5489f8 100644 --- a/src/main/java/roomescape/reservation/ReservationResponse.java +++ b/src/main/java/roomescape/reservation/ReservationResponse.java @@ -1,12 +1,11 @@ package roomescape.reservation; -public class ReservationResponse { - private Long id; - private String name; - private String theme; - private String date; - private String time; - +public record ReservationResponse( + Long id, + String name, + String theme, + String date, + String time) { public ReservationResponse(Long id, String name, String theme, String date, String time) { this.id = id; this.name = name; diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java index bd3313328..a8f02ddf8 100644 --- a/src/main/java/roomescape/reservation/ReservationService.java +++ b/src/main/java/roomescape/reservation/ReservationService.java @@ -1,30 +1,85 @@ package roomescape.reservation; +import java.util.ArrayList; +import java.util.stream.Collectors; import org.springframework.stereotype.Service; import java.util.List; +import roomescape.member.Member; +import roomescape.member.MemberRepository; +import roomescape.waiting.Waiting; +import roomescape.waiting.WaitingRepository; +import roomescape.waiting.WaitingResponse; +import roomescape.waiting.WaitingWithRank; @Service public class ReservationService { - private ReservationDao reservationDao; + private ReservationRepository reservationRepository; + private WaitingRepository waitingRepository; + private MemberRepository memberRepository; - public ReservationService(ReservationDao reservationDao) { - this.reservationDao = reservationDao; + public ReservationService(ReservationRepository reservationRepository, WaitingRepository waitingRepository, MemberRepository memberRepository) { + this.reservationRepository = reservationRepository; + this.waitingRepository = waitingRepository; + this.memberRepository = memberRepository; } - public ReservationResponse save(ReservationRequest reservationRequest) { - Reservation reservation = reservationDao.save(reservationRequest); + private List waitingList = new ArrayList<>(); - return new ReservationResponse(reservation.getId(), reservationRequest.getName(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); + public Reservation save(Reservation reservation) { + return reservationRepository.save(reservation); } public void deleteById(Long id) { - reservationDao.deleteById(id); + reservationRepository.deleteById(id); } public List findAll() { - return reservationDao.findAll().stream() - .map(it -> new ReservationResponse(it.getId(), it.getName(), it.getTheme().getName(), it.getDate(), it.getTime().getValue())) + return reservationRepository.findAll().stream() + .map(it -> new ReservationResponse( + it.getId(), + it.getName(), + it.getTheme().getName(), + it.getDate(), + it.getTime().getValue() + )) .toList(); } + + public List findMyReservations(String memberName) { + Member member = memberRepository.findMemberByName(memberName); + return findMyReservationsById(member.getId()); + } + public List findMyReservationsById(Long memberId) { + List waitings = waitingRepository.findWaitingsWithRankByMemberId(memberId); + + return waitings.stream() + .map(waitingWithRank -> new MyReservationResponse( + (long) waitingWithRank.getWaiting().getId(), + waitingWithRank.getWaiting().getTheme(), + waitingWithRank.getWaiting().getDate(), + waitingWithRank.getWaiting().getTime(), + waitingWithRank.getRank()+1 + "번째 예약대기")) + .collect(Collectors.toList()); + } + + public WaitingResponse createWaiting(String date, String time, String theme, Long memberId) { + Waiting waiting = new Waiting(); + waiting.setDate(date); + waiting.setTime(time); + waiting.setTheme(theme); + waiting.setMemberId(memberId); + + Waiting savedWaiting = waitingRepository.save(waiting); + + Long rank = waitingRepository.countByThemeAndDateAndTimeAndIdLessThan(theme, date, time, savedWaiting.getId()); + + return new WaitingResponse( + savedWaiting.getId(), + savedWaiting.getTheme(), + savedWaiting.getDate(), + savedWaiting.getTime(), + (rank + 1) + "번째 예약대기" + ); + } } diff --git a/src/main/java/roomescape/theme/Theme.java b/src/main/java/roomescape/theme/Theme.java index 430a6239c..4116a8a36 100644 --- a/src/main/java/roomescape/theme/Theme.java +++ b/src/main/java/roomescape/theme/Theme.java @@ -1,6 +1,13 @@ package roomescape.theme; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +@Entity public class Theme { + @Id + @GeneratedValue(strategy = jakarta.persistence.GenerationType.IDENTITY) private Long id; private String name; private String description; diff --git a/src/main/java/roomescape/time/Time.java b/src/main/java/roomescape/time/Time.java index 008ed93cf..caf081ade 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.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +@Entity public class Time { + @Id + @GeneratedValue(strategy = jakarta.persistence.GenerationType.IDENTITY) private Long id; + + @Column(name = "time_value") private String value; public Time(Long id, String value) { @@ -24,4 +34,8 @@ public Long getId() { public String getValue() { return value; } + + public String getTime() { + return value; + } } diff --git a/src/main/java/roomescape/time/TimeDao.java b/src/main/java/roomescape/time/TimeDao.java deleted file mode 100644 index f39a9a328..000000000 --- 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