Skip to content

Commit

Permalink
[3 - 4단계 방탈출 예약 대기] 카피(김상혁) 미션 제출합니다. (#107)
Browse files Browse the repository at this point in the history
* fix: 불필요 ddl 컬럼 삭제

* refactor: jpa 자동 ddl 생성 none 설정

* feat: 3단계 클라이언트 코드 추가

* chore: jpa ddl auto를 validate로 변경

* refactor: 예약 여부 객체 이름 명확히 변경

* feat: 예약 엔티티에 예약 상태 필드 추가 및 예약 생성 시 예약,대기 여부 받도록 변경

* feat: 예약 대기 요청 api 구현

* feat: 예약 상태 출력 시 예약, 예약대기를 구분하여 출력하는 기능 구현

* feat: 이미 요청한 예약, 예약대기가 존재하는지 검증하는 기능 구현

* feat: 예약페이지 시간 출력 시 시,분만 출력되도록 기능 추가

* feat: 기본 예약 대기 데이터 추가

* feat: 예약 대기 취소 api 구현

* refactor: 예약 대기 api 엔드포인트 waitings로 변경

* refactor: 예약 삭제 api 메서드 명 올바르게 수정

* feat: 모든 엔티티 생성시간 저장하도록 컬럼 추가

* refactor: 작성 쿼리 가독성있게 변경

* feat: 내 예약 목록의 예약 대기 상태에 몇번째 대기인지 포함하는 기능 구현

* feat: 4단계 예약 대기 관리 기능 클라이언트 코드 추가

* feat: 예약 대기 관리 페이지 렌더링 기능 구현

* refactor: 예약대기 엔드포인트 waiting 으로 변경

* feat: 예약 대기 목록 조회하는 api 구현

* feat: 어드민 예약 대기 조회 페이지 네비게이션 바 추가

* feat: 예약 대기 취소 api 구현

* feat: 예약 취소 발생시 예약 대기 자동으로 승인하는 기능 구현

* style: 전체 코드 가독성 있게 줄바꿈 적용

* refactor: api 엔드포인트 맨앞에 /api를 붙여 구분하도록 변경

* feat: 예약 삭제를 관리자만 가능하도록 기능 추가

* feat: 예약 시간 추가, 삭제를 관리자만 가능하도록 기능 추가

* feat: 테마 추가, 삭제를 관리자만 가능하도록 기능 추가

* test: 실패하는 테스트 수정

* feat: 예약 대기 삭제 시 본인, 관리자만 삭제할 수 있도록 기능 추가

* refactor: 특정 날짜, 테마의 시간 별 예약 여부를 찾는 메서드 이름 명확히 변경

* refactor: 예약 상태 변경 메서드 파라미터 받아서 적용하도록 변경

* refactor: enum 비교 ==로 하도록 변경

* refactor: User를 Member로 명명 통일

* refactor: 서비스 CRD로 분류하지 않고 하나로 통일

* refactor: 예약, 대기 요청 여부을 클라이언트에서 request로 넘기도록 변경하여 하나의 예약 api에서 처리하도록 변경

* feat: 사용자가 예약 삭제 시 예약 대기만 삭제할 수 있도록 검증

* refactor: 사용자 예약 대기 취소 api 엔드포인트 reservations으로 변경

* fix: 잘못된 테마 삭제 엔드포인트 수정

* refactor: 모든 예약, 예약 대기 삭제 api를 예약 삭제 api 하나로 통일

* refactor: 관리자 예약 목록 리스트 조회 api 엔드포인트 리소스를 예약으로 변경

* refactor: 중복되는 api RequestMapping으로 공통 처리

* refactor: 예약 대기 목록 조회 메서드 대신 예약 상태에 따라 조회할 수 있도록 변경

* refactor: 예약 여부 반환하는 메서드 equals 대신 contains 사용

* style: sql 들여쓰기

* refactor: reservation 버튼 클릭시 홈으로 가지 않고 예약 페이지로 이동하도록 변경
  • Loading branch information
tkdgur0906 committed May 27, 2024
1 parent fcf4785 commit 15a18e3
Show file tree
Hide file tree
Showing 97 changed files with 1,549 additions and 902 deletions.
2 changes: 2 additions & 0 deletions src/main/java/roomescape/RoomescapeApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class RoomescapeApplication {
public static void main(String[] args) {
SpringApplication.run(RoomescapeApplication.class, args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ public class AdminAuthHandlerInterceptor implements HandlerInterceptor {
private final AuthService authService;
private final TokenProvider tokenProvider;

public AdminAuthHandlerInterceptor(AuthService authService, TokenProvider tokenProvider) {
public AdminAuthHandlerInterceptor(AuthService authService,
TokenProvider tokenProvider) {
this.authService = authService;
this.tokenProvider = tokenProvider;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@ public class AuthenticatedMemberArgumentResolver implements HandlerMethodArgumen
private final AuthService authService;
private final TokenProvider tokenProvider;

public AuthenticatedMemberArgumentResolver(AuthService authService, TokenProvider tokenProvider) {
public AuthenticatedMemberArgumentResolver(AuthService authService,
TokenProvider tokenProvider) {
this.authService = authService;
this.tokenProvider = tokenProvider;
}

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(AuthenticatedMember.class)
&& Member.class.isAssignableFrom(parameter.getParameterType());
&& Member.class.isAssignableFrom(parameter.getParameterType());
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/roomescape/config/AuthWebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers)

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(adminAuthHandlerInterceptor).addPathPatterns("/admin/**");
registry.addInterceptor(adminAuthHandlerInterceptor).addPathPatterns("/admin/**", "/api/admin/**");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import roomescape.auth.AuthenticatedMember;
import roomescape.domain.Member;
import roomescape.service.auth.AuthService;
import roomescape.service.dto.request.LoginRequest;
import roomescape.service.dto.response.MemberIdAndNameResponse;

@RequestMapping("/api")
@RestController
public class AuthApiController {

Expand All @@ -29,7 +31,9 @@ public ResponseEntity<MemberIdAndNameResponse> getMemberLoginInfo(@Authenticated
}

@PostMapping("/login")
public ResponseEntity<Void> login(@RequestBody @Valid LoginRequest request, HttpServletResponse response) {
public ResponseEntity<Void> login(@RequestBody @Valid
LoginRequest request,
HttpServletResponse response) {
String accessToken = authService.login(request);
Cookie cookie = new Cookie("token", accessToken);
cookie.setHttpOnly(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ public MemberApiController(MemberService memberService) {
this.memberService = memberService;
}

@PostMapping("/members")
public ResponseEntity<MemberIdAndNameResponse> signup(@RequestBody @Valid SignupRequest request) {
@PostMapping("/api/members")
public ResponseEntity<MemberIdAndNameResponse> signup(@RequestBody @Valid
SignupRequest request) {
Member member = memberService.signUp(request);
return ResponseEntity.created(URI.create("/members/" + member.getId()))
return ResponseEntity.created(URI.create("/api/members/" + member.getId()))
.body(new MemberIdAndNameResponse(member.getId(), member.getName()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,35 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import roomescape.auth.AuthenticatedMember;
import roomescape.domain.Member;
import roomescape.domain.Reservation;
import roomescape.domain.ReservationStatus;
import roomescape.domain.ReservationWaitingWithRank;
import roomescape.service.dto.request.ReservationSaveRequest;
import roomescape.service.dto.response.MemberReservationResponse;
import roomescape.service.dto.response.ReservationResponse;
import roomescape.service.dto.response.UserReservationResponse;
import roomescape.service.reservation.ReservationCreateService;
import roomescape.service.reservation.ReservationDeleteService;
import roomescape.service.reservation.ReservationFindService;
import roomescape.service.reservation.ReservationService;

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

@Validated
@RequestMapping("/api")
@RestController
public class ReservationApiController {

private final ReservationCreateService reservationCreateService;
private final ReservationFindService reservationFindService;
private final ReservationDeleteService reservationDeleteService;
private final ReservationService reservationService;

public ReservationApiController(ReservationCreateService reservationCreateService,
ReservationFindService reservationFindService,
ReservationDeleteService reservationDeleteService) {
this.reservationCreateService = reservationCreateService;
this.reservationFindService = reservationFindService;
this.reservationDeleteService = reservationDeleteService;
public ReservationApiController(ReservationService reservationService) {
this.reservationService = reservationService;
}

@GetMapping("/reservations")
public ResponseEntity<List<ReservationResponse>> getReservations() {
List<Reservation> reservations = reservationFindService.findReservations();
List<Reservation> reservations = reservationService.findReservations();
return ResponseEntity.ok(
reservations.stream()
.map(ReservationResponse::new)
Expand All @@ -50,27 +46,34 @@ public ResponseEntity<List<ReservationResponse>> getReservations() {
}

@GetMapping("/reservations-mine")
public ResponseEntity<List<UserReservationResponse>> getUserReservations(@AuthenticatedMember Member member) {
List<Reservation> userReservations = reservationFindService.findUserReservations(member.getId());
public ResponseEntity<List<MemberReservationResponse>> getMemberReservations(@AuthenticatedMember Member member) {
List<ReservationWaitingWithRank> reservationWaitingWithRanks =
reservationService.findMemberReservations(member.getId());
return ResponseEntity.ok(
userReservations.stream()
.map(UserReservationResponse::new)
reservationWaitingWithRanks.stream()
.map(MemberReservationResponse::new)
.toList()
);
}

@PostMapping("/reservations")
public ResponseEntity<ReservationResponse> addReservationByUser(@RequestBody @Valid ReservationSaveRequest request,
@AuthenticatedMember Member member) {
Reservation newReservation = reservationCreateService.createReservation(request, member);
return ResponseEntity.created(URI.create("/reservations/" + newReservation.getId()))
public ResponseEntity<ReservationResponse> addReservationByMember(@RequestBody @Valid
ReservationSaveRequest request,
@AuthenticatedMember Member member) {
Reservation newReservation = reservationService.createReservation(
request,
member
);
return ResponseEntity.created(URI.create("/api/reservations/" + newReservation.getId()))
.body(new ReservationResponse(newReservation));
}

@DeleteMapping("/reservations/{id}")
public ResponseEntity<Void> deleteReservation(@PathVariable
@Positive(message = "1 이상의 값만 입력해주세요.") long id) {
reservationDeleteService.deleteReservation(id);
public ResponseEntity<Void> deleteWaiting(@AuthenticatedMember Member member,
@PathVariable
@Positive(message = "1 이상의 값만 입력해주세요.")
long id) {
reservationService.deleteReservation(id, member);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
@@ -1,84 +1,57 @@
package roomescape.controller.api;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Positive;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import roomescape.domain.ReservationStatus;
import roomescape.domain.BookingStatus;
import roomescape.domain.ReservationTime;
import roomescape.service.dto.request.ReservationTimeSaveRequest;
import roomescape.service.dto.response.ReservationStatusResponse;
import roomescape.service.dto.response.BookingStatusResponse;
import roomescape.service.dto.response.ReservationTimeResponse;
import roomescape.service.reservationtime.ReservationTimeCreateService;
import roomescape.service.reservationtime.ReservationTimeDeleteService;
import roomescape.service.reservationtime.ReservationTimeFindService;
import roomescape.service.reservationtime.ReservationTimeService;

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

@Validated
@RequestMapping("/api/times")
@RestController
public class ReservationTimeApiController {

private final ReservationTimeCreateService reservationTimeCreateService;
private final ReservationTimeFindService reservationTimeFindService;
private final ReservationTimeDeleteService reservationTimeDeleteService;
private final ReservationTimeService reservationTimeService;

public ReservationTimeApiController(ReservationTimeCreateService reservationTimeCreateService,
ReservationTimeFindService reservationTimeFindService,
ReservationTimeDeleteService reservationTimeDeleteService) {
this.reservationTimeCreateService = reservationTimeCreateService;
this.reservationTimeFindService = reservationTimeFindService;
this.reservationTimeDeleteService = reservationTimeDeleteService;
public ReservationTimeApiController(ReservationTimeService reservationTimeService) {
this.reservationTimeService = reservationTimeService;
}

@GetMapping("/times")
@GetMapping
public ResponseEntity<List<ReservationTimeResponse>> getReservationTimes() {
List<ReservationTime> reservationTimes = reservationTimeFindService.findReservationTimes();
List<ReservationTime> reservationTimes = reservationTimeService.findReservationTimes();
return ResponseEntity.ok(
reservationTimes.stream()
.map(ReservationTimeResponse::new)
.toList()
);
}

@GetMapping("/times/available")
public ResponseEntity<List<ReservationStatusResponse>> getReservationTimesIsBooked(
@RequestParam LocalDate date,
@RequestParam @Positive(message = "1 이상의 값만 입력해주세요.") long themeId) {
ReservationStatus reservationStatus = reservationTimeFindService.findIsBooked(date, themeId);
@GetMapping("/available")
public ResponseEntity<List<BookingStatusResponse>> getReservationTimesIsBooked(@RequestParam LocalDate date,
@RequestParam
@Positive(message = "1 이상의 값만 입력해주세요.")
long themeId) {
BookingStatus bookingStatus = reservationTimeService.findTimeSlotsBookingStatus(date, themeId);
return ResponseEntity.ok(
reservationStatus.getReservationStatus()
bookingStatus.getReservationStatus()
.entrySet()
.stream()
.map(status -> new ReservationStatusResponse(
.map(status -> new BookingStatusResponse(
status.getKey(),
status.getValue()
)
).toList()
);
}

@PostMapping("/times")
public ResponseEntity<ReservationTimeResponse> addReservationTime(
@RequestBody @Valid ReservationTimeSaveRequest request) {
ReservationTime reservationTime = reservationTimeCreateService.createReservationTime(request);
return ResponseEntity.created(URI.create("times/" + reservationTime.getId()))
.body(new ReservationTimeResponse(reservationTime));
}

@DeleteMapping("/times/{id}")
public ResponseEntity<Void> deleteReservationTime(@PathVariable
@Positive(message = "1 이상의 값만 입력해주세요.") long id) {
reservationTimeDeleteService.deleteReservationTime(id);
return ResponseEntity.noContent().build();
}
}
51 changes: 12 additions & 39 deletions src/main/java/roomescape/controller/api/ThemeApiController.java
Original file line number Diff line number Diff line change
@@ -1,71 +1,44 @@
package roomescape.controller.api;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Positive;
import java.net.URI;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import roomescape.domain.Theme;
import roomescape.service.dto.request.ThemeSaveRequest;
import roomescape.service.dto.response.ThemeResponse;
import roomescape.service.theme.ThemeCreateService;
import roomescape.service.theme.ThemeDeleteService;
import roomescape.service.theme.ThemeFindService;
import roomescape.service.theme.ThemeService;

import java.util.List;

@Validated
@RequestMapping("/api/themes")
@RestController
public class ThemeApiController {

private final ThemeCreateService themeCreateService;
private final ThemeFindService themeFindService;
private final ThemeDeleteService themeDeleteService;
private final ThemeService themeService;

public ThemeApiController(ThemeCreateService themeCreateService,
ThemeFindService themeFindService,
ThemeDeleteService themeDeleteService) {
this.themeCreateService = themeCreateService;
this.themeFindService = themeFindService;
this.themeDeleteService = themeDeleteService;
public ThemeApiController(ThemeService themeService) {
this.themeService = themeService;
}

@GetMapping("/themes")
@GetMapping
public ResponseEntity<List<ThemeResponse>> getThemes() {
List<Theme> themes = themeFindService.findThemes();
List<Theme> themes = themeService.findThemes();
return ResponseEntity.ok(
themes.stream()
.map(ThemeResponse::new)
.toList()
);
}

@GetMapping("/themes/ranks")
@GetMapping("/ranks")
public ResponseEntity<List<ThemeResponse>> getThemeRanks() {
List<Theme> themes = themeFindService.findThemeRanks();
List<Theme> themes = themeService.findThemeRanks();
return ResponseEntity.ok(
themes.stream()
.map(ThemeResponse::new)
.toList()
);
}

@PostMapping("/themes")
public ResponseEntity<ThemeResponse> addTheme(@RequestBody @Valid ThemeSaveRequest request) {
Theme theme = themeCreateService.createTheme(request);
return ResponseEntity.created(URI.create("/themes/" + theme.getId()))
.body(new ThemeResponse(theme));
}

@DeleteMapping("/themes/{id}")
public ResponseEntity<Void> deleteTheme(@PathVariable
@Positive(message = "1 이상의 값만 입력해주세요.") long id) {
themeDeleteService.deleteTheme(id);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public AdminMemberApiController(MemberService memberService) {
this.memberService = memberService;
}

@GetMapping("/admin/members")
@GetMapping("/api/admin/members")
public ResponseEntity<List<MemberIdAndNameResponse>> getMembers() {
List<Member> members = memberService.findMembers();
return ResponseEntity.ok(
Expand Down
Loading

0 comments on commit 15a18e3

Please sign in to comment.