Skip to content

Commit

Permalink
feat: 시작 날짜를 필터로 전체 예약 조회 기능 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
nuyh99 committed Jan 13, 2024
1 parent ad61160 commit f590f88
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.example.busan.auth.dto.Authentication;
import com.example.busan.reservation.dto.CancelReservationRequest;
import com.example.busan.reservation.dto.CreateReservationRequest;
import com.example.busan.reservation.dto.FindReservationRequest;
import com.example.busan.reservation.dto.ReservationResponse;
import com.example.busan.reservation.dto.UpdateReservationRequest;
import com.example.busan.reservation.service.ReservationService;
Expand Down Expand Up @@ -34,6 +35,7 @@ public ReservationController(final ReservationService reservationService) {
@PostMapping
public ResponseEntity<Void> create(@RequestBody final CreateReservationRequest request) {
reservationService.create(request);

return ResponseEntity.status(HttpStatus.CREATED).build();
}

Expand All @@ -42,6 +44,7 @@ public ResponseEntity<Void> delete(@PathVariable("reservationId") final Long id,
@Authorized final Authentication authentication,
@RequestBody final CancelReservationRequest request) {
reservationService.deleteById(id, authentication.email(), request);

return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}

Expand All @@ -50,13 +53,25 @@ public ResponseEntity<Void> update(@PathVariable("reservationId") final Long id,
@Authorized final Authentication authentication,
@RequestBody final UpdateReservationRequest request) {
reservationService.update(id, authentication.email(), request);

return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}

@GetMapping
public ResponseEntity<Page<ReservationResponse>> findAll(@Authorized final Authentication authentication,
@PageableDefault final Pageable pageable) {
final Page<ReservationResponse> response = reservationService.findAll(authentication.email(), pageable);
public ResponseEntity<Page<ReservationResponse>> findAllByCurrentLoggedInMember(
@Authorized final Authentication authentication,
@PageableDefault final Pageable pageable) {
final Page<ReservationResponse> response = reservationService.findAllByCurrentLoggedInMember(
authentication.email(), pageable);

return ResponseEntity.ok(response);
}

@GetMapping("/all")
public ResponseEntity<Page<ReservationResponse>> findAll(@PageableDefault final Pageable pageable,
final FindReservationRequest request) {
final Page<ReservationResponse> reservations = reservationService.findAll(request, pageable);

return ResponseEntity.ok(reservations);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,6 @@ boolean existDuplicatedTime(@Param("startTime") LocalDateTime startTime,
Optional<Reservation> findByIdAndReservationEmailAndStatusIsNot(Long id, String email, Status status);

Page<Reservation> findAllByReservationEmail(String reservationEmail, Pageable pageable);

Page<Reservation> findAllByStartTimeBetween(LocalDateTime startInclusive, LocalDateTime endInclusive, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.busan.reservation.dto;

import java.time.LocalDateTime;

public record FindReservationRequest(LocalDateTime start, LocalDateTime end) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.example.busan.reservation.domain.Status;
import com.example.busan.reservation.dto.CancelReservationRequest;
import com.example.busan.reservation.dto.CreateReservationRequest;
import com.example.busan.reservation.dto.FindReservationRequest;
import com.example.busan.reservation.dto.ReservationResponse;
import com.example.busan.reservation.dto.UpdateReservationRequest;
import com.example.busan.room.domain.Room;
Expand Down Expand Up @@ -70,7 +71,7 @@ public void update(final Long id, final String currentMemberEmail, final UpdateR
reservation.update(request.roomId(), request.startTime(), request.endTime());
}

public Page<ReservationResponse> findAll(final String currentMemberEmail, final Pageable pageable) {
public Page<ReservationResponse> findAllByCurrentLoggedInMember(final String currentMemberEmail, final Pageable pageable) {
final Member member = getMember(currentMemberEmail);
final Page<Reservation> reservations = getReservations(currentMemberEmail, pageable);
final Map<Long, Room> rooms = getRooms(reservations.getContent());
Expand Down Expand Up @@ -100,4 +101,28 @@ private Map<Long, Room> getRooms(final List<Reservation> reservations) {
return roomRepository.findAllById(roomIds).stream()
.collect(toMap(Room::getId, Function.identity()));
}

public Page<ReservationResponse> findAll(final FindReservationRequest request, final Pageable pageable) {
final PageRequest pageRequest = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by("startTime"));
final Page<Reservation> reservations = reservationRepository.findAllByStartTimeBetween(
request.start(), request.end(), pageRequest);

final Map<Long, Room> rooms = getRooms(reservations.getContent());
final Map<String, Member> members = getReservedMembers(reservations);

return reservations.map(reservation -> ReservationResponse.of(
reservation,
members.get(reservation.getReservationEmail()),
rooms.get(reservation.getRoomId())));
}

private Map<String, Member> getReservedMembers(final Page<Reservation> reservations) {
final List<String> emails = reservations.getContent().stream()
.map(Reservation::getReservationEmail)
.distinct()
.toList();

return memberRepository.findAllById(emails).stream()
.collect(toMap(Member::getEmail, Function.identity()));
}
}
74 changes: 66 additions & 8 deletions src/main/resources/static/api/openapi3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ paths:
현재 유저 정보 조회하기:
value: "{\"name\":\"연어\",\"phone\":\"01012341234\",\"email\":\"\
[email protected]\",\"role\":\"USER\",\"company\":\"우형\",\"region\"\
:\"BUSAN\",\"createdAt\":\"2024-01-13T17:45:12.402886\"}"
:\"BUSAN\",\"createdAt\":\"2024-01-13T18:38:16.899219\"}"
post:
tags:
- members
Expand Down Expand Up @@ -276,18 +276,18 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/reservations-2055281597'
$ref: '#/components/schemas/reservations-all-2055281597'
examples:
자신의 회의실 예약 목록 최신 순으로 보기:
value: "{\"content\":[{\"id\":1,\"status\":\"RESERVED\",\"cancelReason\"\
:null,\"startTime\":\"2024-01-13T17:45:14.276581\",\"endTime\"\
:\"2024-01-13T19:45:14.276586\",\"name\":\"황재현\",\"phone\":\"\
01012341234\",\"reservedAt\":\"2024-01-13T17:45:14.276596\",\"\
:null,\"startTime\":\"2024-01-13T18:38:18.68911\",\"endTime\"\
:\"2024-01-13T20:38:18.689115\",\"name\":\"황재현\",\"phone\":\"\
01012341234\",\"reservedAt\":\"2024-01-13T18:38:18.689124\",\"\
roomId\":1,\"roomName\":\"대회의실\",\"company\":\"요기요\"},{\"id\"\
:2,\"status\":\"CANCELED\",\"cancelReason\":\"쓰기 싫어졌어요..\",\"\
startTime\":\"2024-01-13T17:45:14.276604\",\"endTime\":\"2024-01-13T19:45:14.276606\"\
startTime\":\"2024-01-13T18:38:18.689133\",\"endTime\":\"2024-01-13T20:38:18.689134\"\
,\"name\":\"황재현\",\"phone\":\"01012341234\",\"reservedAt\":\"\
2024-01-13T17:45:14.276608\",\"roomId\":1,\"roomName\":\"대회의실\"\
2024-01-13T18:38:18.689137\",\"roomId\":1,\"roomName\":\"대회의실\"\
,\"company\":\"토스뱅크\"}],\"pageable\":\"INSTANCE\",\"last\":true,\"\
totalPages\":1,\"totalElements\":2,\"first\":true,\"size\":2,\"\
number\":0,\"sort\":{\"empty\":true,\"sorted\":false,\"unsorted\"\
Expand All @@ -308,6 +308,55 @@ paths:
responses:
"201":
description: "201"
/reservations/all:
get:
tags:
- reservations
operationId: 전체 회의실 예약 목록 최신 순으로 보기
parameters:
- name: page
in: query
description: 페이지는 1부터 시작 (디폴트값 1)
required: false
schema:
type: string
- name: size
in: query
description: 페이지별 사이즈 (디폴트값 10)
required: false
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/reservations-all1582432150'
examples:
전체 회의실 예약 목록 최신 순으로 보기:
value: "{\"start\":\"2024-01-13T18:38:18.800506\",\"end\":\"2024-01-13T18:38:18.800508\"\
}"
responses:
"200":
description: "200"
content:
application/json:
schema:
$ref: '#/components/schemas/reservations-all-2055281597'
examples:
전체 회의실 예약 목록 최신 순으로 보기:
value: "{\"content\":[{\"id\":1,\"status\":\"RESERVED\",\"cancelReason\"\
:null,\"startTime\":\"2024-01-13T18:38:18.800459\",\"endTime\"\
:\"2024-01-13T20:38:18.800463\",\"name\":\"황재현\",\"phone\":\"\
01012341234\",\"reservedAt\":\"2024-01-13T18:38:18.80047\",\"\
roomId\":1,\"roomName\":\"대회의실\",\"company\":\"요기요\"},{\"id\"\
:2,\"status\":\"CANCELED\",\"cancelReason\":\"쓰기 싫어졌어요..\",\"\
startTime\":\"2024-01-13T18:38:18.800473\",\"endTime\":\"2024-01-13T20:38:18.800474\"\
,\"name\":\"황재현\",\"phone\":\"01012341234\",\"reservedAt\":\"\
2024-01-13T18:38:18.800477\",\"roomId\":1,\"roomName\":\"대회의실\"\
,\"company\":\"토스뱅크\"}],\"pageable\":\"INSTANCE\",\"last\":true,\"\
totalPages\":1,\"totalElements\":2,\"first\":true,\"size\":2,\"\
number\":0,\"sort\":{\"empty\":true,\"sorted\":false,\"unsorted\"\
:true},\"numberOfElements\":2,\"empty\":false}"
/reservations/{reservationId}:
put:
tags:
Expand Down Expand Up @@ -441,6 +490,15 @@ components:
isDuplicated:
type: boolean
description: 중복 여부
reservations-all1582432150:
type: object
properties:
start:
type: string
description: 시작일(포함)
end:
type: string
description: 마지막일(포함)
rooms1328389190:
type: array
items:
Expand Down Expand Up @@ -568,7 +626,7 @@ components:
reason:
type: string
description: 취소 사유
reservations-2055281597:
reservations-all-2055281597:
type: object
properties:
number:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.example.busan.reservation.domain.Status;
import com.example.busan.reservation.dto.CancelReservationRequest;
import com.example.busan.reservation.dto.CreateReservationRequest;
import com.example.busan.reservation.dto.FindReservationRequest;
import com.example.busan.reservation.dto.ReservationResponse;
import com.example.busan.reservation.dto.UpdateReservationRequest;
import com.example.busan.reservation.service.ReservationService;
Expand Down Expand Up @@ -125,8 +126,8 @@ void update() throws Exception {
}

@Test
@DisplayName("회의실 예약 목록 조회하기")
void findAll() throws Exception {
@DisplayName("자신의 회의실 예약 목록 조회하기")
void findAllByCurrentLoggedInMember() throws Exception {
//given
httpSession.setAttribute(AuthController.AUTHORIZATION, new Authentication("[email protected]", Role.USER));

Expand All @@ -137,7 +138,7 @@ void findAll() throws Exception {
2L, Status.CANCELED, "쓰기 싫어졌어요..", LocalDateTime.now(), LocalDateTime.now().plusHours(2),
"황재현", "01012341234", LocalDateTime.now(), 1L, "대회의실", "토스뱅크");

given(reservationService.findAll(any(), any()))
given(reservationService.findAllByCurrentLoggedInMember(any(), any()))
.willReturn(new PageImpl<>(List.of(reservation1, reservation2)));

//when
Expand Down Expand Up @@ -179,4 +180,65 @@ void findAll() throws Exception {
//then
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
}

@Test
@DisplayName("전체 회의실 예약 목록 조회하기")
void findAll() throws Exception {
//given
final ReservationResponse reservation1 = new ReservationResponse(
1L, Status.RESERVED, null, LocalDateTime.now(), LocalDateTime.now().plusHours(2),
"황재현", "01012341234", LocalDateTime.now(), 1L, "대회의실", "요기요");
final ReservationResponse reservation2 = new ReservationResponse(
2L, Status.CANCELED, "쓰기 싫어졌어요..", LocalDateTime.now(), LocalDateTime.now().plusHours(2),
"황재현", "01012341234", LocalDateTime.now(), 1L, "대회의실", "토스뱅크");
final String request = objectMapper.writeValueAsString(
new FindReservationRequest(LocalDateTime.now(), LocalDateTime.now()));

given(reservationService.findAll(any(), any()))
.willReturn(new PageImpl<>(List.of(reservation1, reservation2)));

//when
final MockHttpServletResponse response = mockMvc.perform(
get("/reservations/all")
.queryParam("page", "1")
.queryParam("size", "10")
.session(httpSession)
.content(request)
.contentType(APPLICATION_JSON))
.andDo(print())
.andDo(document("전체 회의실 예약 목록 최신 순으로 보기",
queryParameters(
parameterWithName("page").description("페이지는 1부터 시작 (디폴트값 1)").optional(),
parameterWithName("size").description("페이지별 사이즈 (디폴트값 10)").optional()),
requestFields(
fieldWithPath("start").description("시작일(포함)"),
fieldWithPath("end").description("마지막일(포함)")),
responseFields(
fieldWithPath("content.[].id").description("예약 ID"),
fieldWithPath("content.[].status").description("예약 상태"),
fieldWithPath("content.[].cancelReason").description("취소 상태일 경우 취소 이유(취소가 아니면 null)").optional(),
fieldWithPath("content.[].startTime").description("시작 시각"),
fieldWithPath("content.[].endTime").description("종료 시각"),
fieldWithPath("content.[].name").description("예약자 성함"),
fieldWithPath("content.[].phone").description("예약자 휴대폰 번호"),
fieldWithPath("content.[].reservedAt").description("예약 일시"),
fieldWithPath("content.[].roomId").description("회의실 ID"),
fieldWithPath("content.[].company").description("예약자 회사"),
fieldWithPath("content.[].roomName").description("회의실 이름"),
fieldWithPath("last").description("마지막 페이지인지"),
fieldWithPath("first").description("첫 페이지인지"),
fieldWithPath("empty").description("빈 데이터인지"),
fieldWithPath("totalPages").description("전체 페이지 개수"),
fieldWithPath("totalElements").description("전체 DB 데이터 요소 개수"),
fieldWithPath("numberOfElements").description("현재 응답 데이터 요소 개수"),
fieldWithPath("size").description("한 페이지당 사이즈"),
fieldWithPath("number").description("현재 페이지"),
fieldWithPath("pageable").description("요청한 페이지네이션 정보").ignored(),
fieldWithPath("sort.*").description("정렬 정보").ignored())))
.andReturn()
.getResponse();

//then
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,22 @@ void findAllByReservationEmail() {
assertThat(response).hasSize(2)
.isSortedAccordingTo(Comparator.comparing(Reservation::getStartTime).reversed());
}

@Test
@DisplayName("시작일이 두 날짜 사이의 전체 예약을 조회한다")
void findAllByStartTimeBetween() {
reservationRepository.save(new Reservation(1L, LocalDateTime.of(2050, 11, 10, 13, 0), LocalDateTime.of(2050, 11, 10, 15, 30)));
reservationRepository.save(new Reservation(1L, LocalDateTime.of(2070, 11, 10, 13, 1), LocalDateTime.of(2070, 11, 10, 15, 30)));
reservationRepository.save(new Reservation(1L, LocalDateTime.of(2070, 11, 10, 13, 2), LocalDateTime.of(2070, 11, 10, 15, 30)));
final PageRequest pageRequest = PageRequest.of(0, 2, Sort.by("startTime").descending());

//when
final List<Reservation> response = reservationRepository.findAllByStartTimeBetween(
LocalDateTime.of(2050, 11, 10, 13, 0), LocalDateTime.of(2070, 11, 10, 13, 1), pageRequest)
.getContent();

//then
assertThat(response).hasSize(2)
.isSortedAccordingTo(Comparator.comparing(Reservation::getStartTime).reversed());
}
}
Loading

0 comments on commit f590f88

Please sign in to comment.