Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
bdd1386
feat : migration code
youngsu5582 May 14, 2024
4519cc5
feat(ReservationTimeRepository) : JDBC->JPA 변환 구현
youngsu5582 May 15, 2024
744ae68
feat(ThemeRepository) : JDBC->JPA 변환 구현
youngsu5582 May 15, 2024
7b3d930
feat(MemberRepository) : JDBC->JPA 변환 구현
youngsu5582 May 15, 2024
ad235f6
feat(ReservationRepository) : JDBC->JPA 변환 구현
youngsu5582 May 15, 2024
39059a8
feat(ThemeRepository) : 인기 테마 가져오는 기능 JPA 통해 구현
youngsu5582 May 15, 2024
5ef8412
feat(ReservationTimeRepository) : 예약 가능한 시간 받는 기능 구현
youngsu5582 May 15, 2024
27f7154
feat(ReservationTimeRepository) : 예약 가능한 시간 받는 기능 구현
youngsu5582 May 15, 2024
338f91e
feat(MemberService) : Dao -> Repository 교체
youngsu5582 May 15, 2024
708f8b7
feat(ThemeService) : Dao -> Repository 교체
youngsu5582 May 15, 2024
f21b4f0
feat(ReservationTime) : Dao -> Repository 교체
youngsu5582 May 15, 2024
a8de7d6
feat(Reservation) : Dao -> Repository 교체
youngsu5582 May 15, 2024
07f981d
feat(AvailableReservationTime) : record 삭제,이름 변경
youngsu5582 May 15, 2024
224b9bb
refactor(dao,mapper) : JPA 변환으로 삭제
youngsu5582 May 15, 2024
b5df1ee
feat(Member) : 저장시, Enum 문자열 값으로 저장되게 변경
youngsu5582 May 15, 2024
1347476
test : 불필요한 테스트 코드 삭제
youngsu5582 May 15, 2024
515731f
docs : 기능 요구사항 초안 정리
youngsu5582 May 15, 2024
80abf8f
feat(Reservation) : 예약 상태 추가
youngsu5582 May 15, 2024
7ec5d4a
feat(ReservationService): 자신이 예약한 예약들 가져오는 기능 구현
youngsu5582 May 15, 2024
eb25e66
feat(ReservationController): 자신이 예약한 예약들 응답 기능 구현
youngsu5582 May 15, 2024
09c0665
feat(MemberPageController): 내 예약 페이지 응답 기능 구현
youngsu5582 May 15, 2024
460d288
fix(ReservationApiController): API 응답 dto 수정
youngsu5582 May 15, 2024
f0a0e0f
refactor: Inserter 패키지 이동
youngsu5582 May 15, 2024
ab306cb
test: 내 예약 페이지 응답 기능 테스트 구현
youngsu5582 May 15, 2024
e3cebb1
feat : 초기 데이터 설정,스키마 파일 삭제
youngsu5582 May 15, 2024
3eb91ac
test : DB 초기화 로직 변경
youngsu5582 May 15, 2024
6fc0406
feat(Theme) : ID 생성 전략 추가
youngsu5582 May 15, 2024
4e6b328
fix : test 시에도 지연 생성 설정 추가
youngsu5582 May 15, 2024
df19cdf
refactor : dao->repository 로 패키지명 변경
youngsu5582 May 15, 2024
e182a1c
refactor(all) : 코드 자동 정렬
youngsu5582 May 16, 2024
e31488a
refactor(ReservationApiController) : RequestMapping 경로 추가로 다시 경로 응집성 …
youngsu5582 May 16, 2024
903776d
refactor(LoginApiController) : ResponseCookie 로 변경
youngsu5582 May 16, 2024
68862dd
refactor(ThemeApiController) : RestController 로 변경
youngsu5582 May 16, 2024
54c32b4
refactor(All) : 사용하지 않는 불필요한 코드들 제거
youngsu5582 May 16, 2024
68c7e5e
feat(Thumbnail) : 확장자 검사 기능 제거
youngsu5582 May 16, 2024
281bf69
test : SpringBootTest 로 변경
youngsu5582 May 16, 2024
b43aac7
feat(Interceptor) : 인터셉터 순서 지정해 Login->Admin 순으로 동작하게 변경
youngsu5582 May 16, 2024
0737bee
test(Acceptance) : 인수테스트 초기 설정
youngsu5582 May 18, 2024
40da556
refactor(AuthController) : 컨트롤러 명 변경, 기능 이동
youngsu5582 May 18, 2024
fe2a339
feat(MemberService) : 로그인 중 발생하는 예외 변경
youngsu5582 May 18, 2024
9835145
feat(ReservationService) : 삭제한 값이 없을때 예외 발생 기능 구현
youngsu5582 May 18, 2024
f029bf1
test(AuthAcceptanceTest) : 로그인 확인 기능에 대한 인수 테스트 구현
youngsu5582 May 18, 2024
4dc87d9
feat(Member,MemberRepository) : 이메일 문자열로 검색하게 변경, 칼럼명 지정
youngsu5582 May 18, 2024
0152657
feat(Reservation) : 불필요한 생성자내 Id 매개변수 제거
youngsu5582 May 18, 2024
e7d59c6
test : 인수 테스트 전환으로 인해 컨트롤러 테스트 제거
youngsu5582 May 18, 2024
0363cf1
refactor(Member) : 불필요한 정적 팩토리 제거
youngsu5582 May 18, 2024
352cd21
refactor : 빈 생성자 접근 제한자 변경
youngsu5582 May 18, 2024
9467dad
feat(Theme,ReservationTime,Member) : 불필요한 생성자내 Id 매개변수 제거
youngsu5582 May 18, 2024
83797ec
feat(TokenContextRequest) : 불필요한 ScopeBean 삭제
youngsu5582 May 18, 2024
c92b8ba
feat : unique 제약 조건 추가
youngsu5582 May 19, 2024
50a3078
test(Interceptor) : controller->config 패키지로 이동
youngsu5582 May 19, 2024
17ab3e9
feat(ReservationTimeService) : 예약가능 시간 조회 기능 로직 변경
youngsu5582 May 19, 2024
d053f77
refactor(Reservation) : 예약 날짜 검색 메소드명 자연스럽게 변경
youngsu5582 May 19, 2024
a8069e6
fix(Reservation) : 누락된 예약 날짜 검색 메소드명 변경 내역 수정
youngsu5582 May 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## 방탈출 예약 대기

### 기능 요구 사항

- [x] 내 예약 목록을 조회하는 API를 구현한다.

6 changes: 5 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
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 'io.jsonwebtoken:jjwt:0.9.1'
implementation 'javax.xml.bind:jaxb-api:2.3.1'

implementation 'io.jsonwebtoken:jjwt-api:0.11.2'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.2'
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/roomescape/RoomescapeApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

@SpringBootApplication
public class RoomescapeApplication {
public static void main(String[] args) {

public static void main(final String[] args) {
SpringApplication.run(RoomescapeApplication.class, args);
}

Expand Down
22 changes: 22 additions & 0 deletions src/main/java/roomescape/config/CheckAdminInterceptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package roomescape.config;


import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import roomescape.exception.ForbiddenException;
import roomescape.service.dto.output.TokenLoginOutput;

@Component
public class CheckAdminInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
final TokenLoginOutput output = (TokenLoginOutput) request.getAttribute("member");
if (output != null && output.isAdmin()) {
return true;
}
throw new ForbiddenException(request.getRequestURI());
}
}

39 changes: 39 additions & 0 deletions src/main/java/roomescape/config/CheckLoginInterceptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package roomescape.config;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import roomescape.service.MemberService;
import roomescape.service.dto.output.TokenLoginOutput;
import roomescape.util.TokenProvider;

import java.util.List;

@Component
public class CheckLoginInterceptor implements HandlerInterceptor {
private static final List<String> CHECK_LIST = initializeCheckList();
private final MemberService memberService;
private final TokenProvider tokenProvider;

private static List<String> initializeCheckList() {
return List.of("/reservations");
}

public CheckLoginInterceptor(final MemberService memberService, final TokenProvider tokenProvider) {
this.memberService = memberService;
this.tokenProvider = tokenProvider;
}

@Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
if (request.getMethod()
.equals("GET") && CHECK_LIST.contains(request.getRequestURI())) {
return true;
}
final String token = tokenProvider.parseToken(request);
final TokenLoginOutput output = memberService.loginToken(token);
request.setAttribute("member", output);
return true;
}
}
40 changes: 40 additions & 0 deletions src/main/java/roomescape/config/WebMvcConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package roomescape.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import roomescape.controller.LoginMemberArgumentResolver;
import roomescape.service.MemberService;
import roomescape.util.TokenProvider;

import java.util.List;

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
private final MemberService memberService;
private final TokenProvider tokenProvider;

public WebMvcConfiguration(final MemberService memberService, final TokenProvider tokenProvider) {
this.memberService = memberService;
this.tokenProvider = tokenProvider;
}


@Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(new CheckLoginInterceptor(memberService, tokenProvider))
.addPathPatterns("/login/check")
.addPathPatterns("/reservations/**")
.addPathPatterns("/admin/**")
.order(1);
registry.addInterceptor(new CheckAdminInterceptor())
.addPathPatterns("/admin/**")
.order(2);
}

@Override
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new LoginMemberArgumentResolver());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package roomescape.controller;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import roomescape.controller.api.dto.request.LoginMemberRequest;
import roomescape.exception.UnauthorizedException;
import roomescape.service.dto.output.TokenLoginOutput;


@Component
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(final MethodParameter parameter) {
return parameter.getParameterType()
.equals(LoginMemberRequest.class);
}

@Override
public LoginMemberRequest resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer, final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) throws Exception {
final HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
final TokenLoginOutput output = (TokenLoginOutput) request.getAttribute("member");
if (output == null) {
throw new UnauthorizedException();
}
return new LoginMemberRequest(output.id(), output.email(), output.password(), output.name());
}
}
31 changes: 31 additions & 0 deletions src/main/java/roomescape/controller/api/AdminApiController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package roomescape.controller.api;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import roomescape.controller.api.dto.request.AdminReservationRequest;
import roomescape.controller.api.dto.response.ReservationResponse;
import roomescape.service.ReservationService;
import roomescape.service.dto.output.ReservationOutput;

import java.net.URI;

@RestController
@RequestMapping("/admin")
public class AdminApiController {

ReservationService reservationService;

public AdminApiController(final ReservationService reservationService) {
this.reservationService = reservationService;
}

@PostMapping("/reservations")
public ResponseEntity<ReservationResponse> createReservation(@RequestBody final AdminReservationRequest request) {
final ReservationOutput output = reservationService.createReservation(request.toInput());
return ResponseEntity.created(URI.create("/reservations/" + output.id()))
.body(ReservationResponse.toResponse(output));
}
}
57 changes: 57 additions & 0 deletions src/main/java/roomescape/controller/api/AuthApiController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package roomescape.controller.api;

import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import roomescape.controller.api.dto.request.LoginMemberRequest;
import roomescape.controller.api.dto.request.MemberCreateRequest;
import roomescape.controller.api.dto.request.MemberLoginRequest;
import roomescape.controller.api.dto.response.TokenLoginResponse;
import roomescape.service.MemberService;
import roomescape.service.dto.output.MemberCreateOutput;
import roomescape.service.dto.output.MemberLoginOutput;

import java.net.URI;

@RestController
public class AuthApiController {
private final MemberService memberService;

public AuthApiController(final MemberService memberService) {
this.memberService = memberService;
}

@PostMapping("/signup")
public ResponseEntity<Void> createMember(@RequestBody final MemberCreateRequest request) {
final MemberCreateOutput output = memberService.createMember(request.toInput());
return ResponseEntity.created(URI.create("/reservations/" + output.id()))
.build();
}

@PostMapping("/login")
public ResponseEntity<Void> login(@RequestBody final MemberLoginRequest memberLoginRequest, final HttpServletResponse response) {
final MemberLoginOutput output = memberService.loginMember(memberLoginRequest.toInput());
final ResponseCookie cookie = initializeCookie(output.token());

return ResponseEntity.ok()
.header(HttpHeaders.SET_COOKIE, cookie.toString())
.build();
}

private ResponseCookie initializeCookie(final String token) {
return ResponseCookie.from("token", token)
.httpOnly(true)
.path("/")
.build();
}

@GetMapping("/login/check")
public ResponseEntity<TokenLoginResponse> checkLogin(final LoginMemberRequest request) {
return ResponseEntity.ok(new TokenLoginResponse(request.name()));
}
}
24 changes: 24 additions & 0 deletions src/main/java/roomescape/controller/api/MemberApiController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package roomescape.controller.api;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import roomescape.controller.api.dto.response.MembersResponse;
import roomescape.service.MemberService;

@RestController
@RequestMapping("/members")
public class MemberApiController {
private final MemberService memberService;

public MemberApiController(final MemberService memberService) {
this.memberService = memberService;
}

@GetMapping
public ResponseEntity<MembersResponse> getAllMembers() {
final var output = memberService.getAllMembers();
return ResponseEntity.ok(MembersResponse.toResponse(output));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package roomescape.controller.api;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import roomescape.controller.api.dto.request.LoginMemberRequest;
import roomescape.controller.api.dto.request.ReservationRequest;
import roomescape.controller.api.dto.response.MemberReservationsResponse;
import roomescape.controller.api.dto.response.ReservationResponse;
import roomescape.controller.api.dto.response.ReservationsResponse;
import roomescape.service.ReservationService;
import roomescape.service.dto.input.ReservationSearchInput;
import roomescape.service.dto.output.ReservationOutput;

import java.net.URI;
import java.time.LocalDate;
import java.util.List;

@RestController
@RequestMapping("/reservations")
public class ReservationApiController {

private final ReservationService reservationService;

public ReservationApiController(final ReservationService reservationService) {
this.reservationService = reservationService;
}

@PostMapping
public ResponseEntity<ReservationResponse> createReservation(@RequestBody final ReservationRequest reservationRequest,
final LoginMemberRequest loginMemberRequest) {
final ReservationOutput output = reservationService.createReservation(reservationRequest.toInput(loginMemberRequest.id()));
return ResponseEntity.created(URI.create("/reservations/" + output.id()))
.body(ReservationResponse.toResponse(output));
}

@GetMapping
public ResponseEntity<ReservationsResponse> getAllReservations() {
final List<ReservationOutput> outputs = reservationService.getAllReservations();
return ResponseEntity.ok(ReservationsResponse.toResponse(outputs));
}

@GetMapping("/search")
public ResponseEntity<ReservationsResponse> searchReservation(
@RequestParam final long themeId,
@RequestParam final long memberId,
@RequestParam final LocalDate fromDate,
@RequestParam final LocalDate toDate) {
final List<ReservationOutput> outputs = reservationService.searchReservation(new ReservationSearchInput(themeId, memberId, fromDate, toDate));
return ResponseEntity.ok(ReservationsResponse.toResponse(outputs));
}

@GetMapping("/mine")
public ResponseEntity<MemberReservationsResponse> getMyReservations(final LoginMemberRequest loginMemberRequest) {
final List<ReservationOutput> outputs = reservationService.getAllMyReservations(loginMemberRequest.id());
return ResponseEntity.ok(MemberReservationsResponse.toResponse(outputs));
}

@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteReservation(@PathVariable final long id) {
reservationService.deleteReservation(id);
return ResponseEntity.noContent()
.build();
}
}
Loading